diff --git a/.gitignore b/.gitignore index a89b5b49c..bb5546f89 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ -/app/cms /app/mocms /app/curd /app/test /app/demo +/app/wechat /.idea /.vscode /.DS_Store @@ -16,3 +16,5 @@ /public/static/mocms /public/static/curd /public/static/storage +/.trae +/.cursor diff --git a/README.md b/README.md index f6c456669..ac000cda5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@
-

FunAdmin6.X版本 在5.X的基础上优化了许多功能,优化了插件功能,欢迎使用

+

FunAdmin7.X版本, 全新的AI开发框架,MCP工具让您的开发过程起飞

funadmin docker 版本请移步docker仓库

@@ -7,7 +7,45 @@ funadmin docker 版本请移步docker仓库 funadmin前端和webman开发的后台管理系统 -

为梦想而创作:FunAdmin开发框架系统 V6.X最低支持PHP8.1

+- ✨ 核心特性 +- 🚀 智能代码生成:AI自动创建完整的CRUD模板 +- 🔍 智能文件搜索:自动定位相关文件并提供精准修改建议 +- 🎯 自动化流程:一键生成API接口和菜单配置 +- 🧠 上下文理解:AI深度理解项目架构,提供更准确的代码联动 +-🛠️ AI编辑工具配置 +- 支持的AI编辑工具 +```txt + Trae (尽量使用 trae.ai 国外版) + Cursor + Claude Code + Windsurf + Codebubby + 其他支持MCP协议的AI编辑器 +``` +```txt +配置步骤 +第一步:启动FunAdmin项目 +确保你的项目正在运行 执行下面的命令 +``` +``` +php mcp-server.php + +``` + + +第二步:配置AI编辑器 +在你的AI编辑工具的配置文件中添加以下MCP配置: +```json +{ +"mcpServers": { + "funadmin": { + "url":"http://127.0.0.1:8080/mcp" + } + } +} +``` + +

为梦想而创作:FunAdmin开发框架系统 V7.X最低支持PHP8.1

用爱发电,开源不易,您先点右上角 "Star" 支持一下 谢谢!

@@ -15,10 +53,10 @@ funadmin前端和webman开发的后台管理系统 - layui + layui - PHP Version + PHP Version MYSQL @@ -54,7 +92,7 @@ funadmin前端和webman开发的后台管理系统 ## 项目介绍 -FunAdmin 基于thinkphp8.X +Layui2.9.*+requirejs开发权限(RBAC)管理框架,框架中集成了权限管理、模块管理、插件管理、后台支持多主题切换、配置管理、会员管理等常用功能模块,以方便开发者快速构建自己的应用。框架专注于为中小企业提供最佳的行业基础后台框架解决方案,执行效率、扩展性、稳定性值得信赖,操作体验流畅,使用非常优化,欢迎大家使用及进行二次开发。 +FunAdmin 基于thinkphp8.X +Layui2.11.*+requirejs开发权限(RBAC)管理框架,框架中集成了权限管理、模块管理、插件管理、后台支持多主题切换、配置管理、会员管理等常用功能模块,以方便开发者快速构建自己的应用。框架专注于为中小企业提供最佳的行业基础后台框架解决方案,执行效率、扩展性、稳定性值得信赖,操作体验流畅,使用非常优化,欢迎大家使用及进行二次开发。 + 支持ThinkPHP 持续升级框架底层;跟随官网脚步 + 这是一个有趣的后台管理系统,这是可以让你节约时间的系统 @@ -67,7 +105,7 @@ FunAdmin 基于thinkphp8.X +Layui2.9.*+requirejs开发权限(RBAC)管理框架 + 内置 `CURD` 命令行模式,帮助助您快速开发系统 + 模块化:全新的架构和模块化的开发机制,便于灵活扩展和二次开发。 + 强大的表单管理,只需要使用函数即可成就表单 -+ layui采用最新layui2.9.X 框架 ++ layui采用最新layui2.11.X 框架 + 适用范围:可以开发OA、ERP、BPM、CRM、WMS、TMS、MIS、BI、电商平台后台、物流管理系统、快递管理系统、教务管理系统等各类管理软件。 + require.js 模块化开发 一个命令即可打包js,css ; node r.js -o min-backend.js + restful api 接口,接口使用jwt接口验证等 @@ -76,7 +114,7 @@ FunAdmin 基于thinkphp8.X +Layui2.9.*+requirejs开发权限(RBAC)管理框架 ## 环境要求: * 开启静态重写 (必须) -* PHP >= 8.0 +* PHP >= 8.1 * PDO PHP Extension * MBstring PHP Extension * CURL PHP Extension diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 000000000..7415724df --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,3 @@ +/.idea +/.vscode +/.DS_Store \ No newline at end of file diff --git a/app/BaseController.php b/app/BaseController.php index f61b4244f..6dd80cbf9 100644 --- a/app/BaseController.php +++ b/app/BaseController.php @@ -23,6 +23,10 @@ */ abstract class BaseController { + + protected array $noNeedLogin = []; + + protected array $onlyNeedLogin = []; /** * Request实例 * @var \think\Request @@ -73,7 +77,7 @@ protected function initialize() * 验证数据 * @access protected * @param array $data 数据 - * @param string|array $validate 验证器名或者验证规则数组 + * @param mixed $validate 验证器名或者验证规则数组 * @param array $message 提示信息 * @param bool $batch 是否批量验证 * @return array|string|true diff --git a/app/api/.DS_Store b/app/api/.DS_Store new file mode 100644 index 000000000..4c8ab94e1 Binary files /dev/null and b/app/api/.DS_Store differ diff --git a/app/api/controller/v1/ApiTest.php b/app/api/controller/v1/ApiTest.php deleted file mode 100644 index 0ee886d4a..000000000 --- a/app/api/controller/v1/ApiTest.php +++ /dev/null @@ -1,43 +0,0 @@ -success('ok'); - - } - -} diff --git a/app/api/controller/v1/Member.php b/app/api/controller/v1/Member.php deleted file mode 100644 index 50cd9aa44..000000000 --- a/app/api/controller/v1/Member.php +++ /dev/null @@ -1,102 +0,0 @@ -success('ok',['member_id'=>$this->member_id]); - //通用参数验证 - } - - /** - * 显示创建资源表单页. - * - * @return \think\Response - */ - public function create() - { - echo "create"; - } - - /** - * 保存新建的资源 - * - * @param \think\Request $request - * @return \think\Response - */ - public function save(Request $request) - { - echo "save"; - } - - /** - * 显示指定的资源 - * - * @param int $id - * @return \think\Response - */ - public function read($id) - { - echo "read"; - } - - /** - * 显示编辑资源表单页. - * - * @param int $id - * @return \think\Response - */ - public function edit($id) - { - echo "edit"; - } - - /** - * 保存更新的资源 - * - * @param \think\Request $request - * @param int $id - * @return \think\Response - */ - public function update(Request $request, $id) - { - echo "update"; - } - - /** - * 删除指定资源 - * - * @param int $id - * @return \think\Response - */ - public function delete($id) - { - echo "delete"; - } - - - public function address($id) - { - echo "address-"; - } -} diff --git a/app/api/controller/v1/Token.php b/app/api/controller/v1/Token.php deleted file mode 100644 index c334fd5e7..000000000 --- a/app/api/controller/v1/Token.php +++ /dev/null @@ -1,42 +0,0 @@ -type).'Token'); - $token = $class::instance(); - $token->build(); - - } - public function refresh(Request $request) - { - $class = ucwords('\\fun\\auth\\'.ucfirst($this->type).'Token'); - $token = $class::instance(); - $token->refresh(); - - } - -} diff --git a/app/api/controller/v2/Member.php b/app/api/controller/v2/Member.php new file mode 100644 index 000000000..233192b6b --- /dev/null +++ b/app/api/controller/v2/Member.php @@ -0,0 +1,36 @@ +success('ok',['user'=>$request->member]); + } + + + public function userinfo(Request $request) + { + $this->success('ok',['user'=>$request->member]); + } + public function verify(Request $request) + { + $this->success('成功'); + + } +} \ No newline at end of file diff --git a/app/api/controller/v2/Token.php b/app/api/controller/v2/Token.php new file mode 100644 index 000000000..698789921 --- /dev/null +++ b/app/api/controller/v2/Token.php @@ -0,0 +1,103 @@ +tokenService = TokenService::instance(); + //跨域 + header('Access-Control-Allow-Origin:*'); + header('Access-Control-Allow-Headers:Accept,Referer,Host,Keep-Alive,User-Agent,X-Requested-With,Cache-Control,Content-Type,Cookie,token'); + header('Access-Control-Allow-Credentials:true'); + header('Access-Control-Allow-Methods:GET, POST, PATCH, PUT, DELETE,OPTIONS'); + if(!$app->request->isPost()){ + $this->error(__('Page not find'),[],404); + } + } + + /** + * 获取token + * @param Request $request + * @return void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function build(Request $request) + { + $username = $request->post('username'); + $password = $request->post('password'); + // 这里应该有用户验证逻辑,例如查询数据库验证用户名和密码 + // 为了示例,假设验证通过 + $member = \app\common\model\Member::where('status', 1) + ->where('username', $username) + ->whereOr('mobile', $username) + ->whereOr('email', $username) + ->field('id ,nickname,username,password') + ->limit(1) + ->find(); + if ($member) { + $member = $member->toArray(); + if (password_verify($password, $member['password'])) { + unset($member['password']); + $accessToken = $this->tokenService->build($member); + $refreshToken = $this->tokenService->build($member, 'refresh'); + $this->success(__('Tokens generated successfully'), [ + 'access_token' => $accessToken, + 'refresh_token' => $refreshToken, + 'expires_in' => config('api.access_token_ttl'), + ]); + } else { + $this->error(lang('Password is not right'), [], 401); + } + } else { + $this->error(lang('Account is not exist'), [], 401); + } + } + + + + /** + * @param Request $request + * @return \think\response\Json + */ + public function refresh(Request $request){ + // 获取 Authorization 头 + if(input('access_token')){ + $refreshToken = input('access_token'); + }else{ + $authHeader = $request->header('Authorization'); + if (!$authHeader || !preg_match('/Bearer\s(\S+)/', $authHeader, $matches)) { + $this->error(__('Unauthorized'), [], 401); + } + $refreshToken = $matches[1]; + } + // 验证 refresh_token + $userData = $this->tokenService->validateToken($refreshToken, 'refresh'); + if (!$userData) { + $this->error(__('Invalid refresh token'), [], 401); + } + // 生成新的 access_token + $newAccessToken = $this->tokenService->build($userData, config('api.access_token_ttl', 3600*2)); + $this->success(__('Access token refreshed successfully'), [ + 'access_token' => $newAccessToken, + ]); + } + +} diff --git a/app/api/middleware.php b/app/api/middleware.php index dfbb96293..a331b565e 100644 --- a/app/api/middleware.php +++ b/app/api/middleware.php @@ -13,9 +13,9 @@ return [ - \think\middleware\LoadLangPack::class, + \think\middleware\LoadLangPack::class, - \think\middleware\SessionInit::class, + \think\middleware\SessionInit::class, //访问频率 // \think\middleware\Throttle::class, //跨域 diff --git a/app/api/route/api.php b/app/api/route/api.php index 0c5dab1dc..c43fe5d78 100644 --- a/app/api/route/api.php +++ b/app/api/route/api.php @@ -13,6 +13,7 @@ use think\facade\Route; ////一般路由规则,访问的url为:v1/member/1,对应的文件为member类下的index方法 Route::get(':version/member/index','api/:version.member/index'); +Route::get(':version/member/userinfo','api/:version.member/userinfo'); // ////资源路由,详情查看tp手册资源路由 //Route::resource(':version/member','api/:version.member'); diff --git a/app/backend/.DS_Store b/app/backend/.DS_Store new file mode 100644 index 000000000..65104179d Binary files /dev/null and b/app/backend/.DS_Store differ diff --git a/app/backend/controller/Addon.php b/app/backend/controller/Addon.php index 2d74446e9..f834d1c75 100644 --- a/app/backend/controller/Addon.php +++ b/app/backend/controller/Addon.php @@ -13,9 +13,6 @@ namespace app\backend\controller; -use app\backend\middleware\CheckRole; -use app\backend\middleware\SystemLog; -use app\backend\middleware\ViewNode; use app\backend\service\AddonService; use app\common\controller\Backend; use app\common\service\AuthCloudService; @@ -30,7 +27,6 @@ use app\common\annotation\NodeAnnotation; use think\facade\Console; use think\facade\Cookie; - /** * @ControllerAnnotation(title="插件管理") * Class Addon @@ -39,26 +35,26 @@ class Addon extends Backend { - protected $middleware = [ - CheckRole::class =>['except'=>['enlang','verify','logout']], - ViewNode::class, - SystemLog::class - ]; - protected $addonService; - protected $authCloudService; - protected $app_version; + protected array $noNeedLogin = ['enlang','verify','logout']; + /** + * @var AddonService + */ + protected AddonService $addonService; + /** + * @var AuthCloudService + */ + protected AuthCloudService $authCloudService; + protected mixed $app_version; public function __construct(App $app) { parent::__construct($app); $this->modelClass = new AddonModel(); - $this->addonService = new AddonService(); - $this->authCloudService = AuthCloudService::instance(); + $this->addonService = app(AddonService::class); + $this->authCloudService = app(AuthCloudService::class); $this->app_version = config('funadmin.version'); } - - /** * @NodeAnnotation(title="列表") * @return mixed|\think\response\Json|\think\response\View @@ -66,24 +62,23 @@ public function __construct(App $app) public function index() { if ($this->request->isAjax()) { + if($this->request->isPost()){ - //登录请求 - $data = $this->request->post(); - $this->authCloudService->setUserParams($data); - $result = $this->authCloudService->setApiUrl('')->setMethod('post') - ->setParams($this->authCloudService->getUserParams()) - ->run(); - if ($result['code'] == 200) { - $this->authCloudService->setAuth($result['data']); - $member = $this->authCloudService->setApiUrl('api/v1.member/get')->setMethod('get') - ->setParams([])->setHeader([$this->authCloudService->authorization=>$result['data']['access_token']])->run(); - $this->authCloudService->setMember($member['data']); - $this->success(lang('login successful'),'',$member['data']); - } else { - $this->error(lang('Login failed:' . $result['msg'])); + try { + $data = $this->request->post(); + // 获取访问令牌 + $tokenResult = $this->authCloudService->getAccessToken($data); + // 获取用户信息 + $member = $this->authCloudService->getMemberInfo($tokenResult['access_token']); + // 设置用户信息并返回成功 + $this->authCloudService->setMember($member); + } catch (Exception $e) { + $this->error(lang('Login failed:' . $e->getMessage())); } + $this->success( lang( 'login successful'),'',$member); + }else{ - $param = $this->request->param(); + $param = input(); list($this->page, $this->pageSize,$sort,$where) = $this->buildParames(); if($where){foreach($where as $k=>$v){$map[$v[0]] = trim($v[2],'%');}} if(empty($param['cateid'])){ @@ -95,25 +90,23 @@ public function index() $map['page'] = $param['page']; $map['limit'] = $param['limit']; unset($map['status']); - $auth = $this->authCloudService->setApiUrl('api/v1.plugins/getList')->setMethod('GET') - ->setParams($map); - if($this->authCloudService->getAuth()){ - $res = $auth->setHeader()->run(); - }else{ - $res = $auth->run(); - } + $res = $this->authCloudService + ->setApiUrl('/api/v2.plugins/getList') + ->setParams($map) + ->setHeader() + ->run(); $list = []; $addonNameArr = []; $addonNameArrAll = []; $count = 1; - if($res['code']==200){ + if (isset($res['code']) && $res['code'] == 200) { $list = $res['data']['list']; $allList = $res['data']['allList']; $addonNameArr = $res['data']['searchNameList']; $addonNameArrAll = $res['data']['nameList']; $count = count($addonNameArr); - }else if($res['code']==401){ - Cookie::set('auth_account',''); + }else if(isset($res['code']) && $res['code']==401){ + $this->authCloudService->setToken()->setMember(); } list($localAddons,$localNameArr) = $this->getLocalAddons(); try { @@ -180,18 +173,18 @@ public function index() unset($value); $result = ['code' => 0, 'msg' => lang('Get Data Success'), 'data' => $addons, 'count' => $count]; - }catch (\Exception $e){ + }catch (Exception $e){ $this->error($e->getMessage()); } return json($result); } } - $res = $this->authCloudService->setApiUrl('api/v1.plugins/cateList')->setMethod('GET') + $res = $this->authCloudService + ->setApiUrl('/api/v2.plugins/cateList') ->setParams([])->run(); $cateList = $res['data']??[]; $account = $this->authCloudService->getMember(); - return view('',[ - 'auth'=>$account?1:0, 'account'=>$account,'cateList'=>$cateList]); + return view('',['auth'=>$account?1:0, 'account'=>$account,'cateList'=>$cateList]); } /** @@ -202,7 +195,7 @@ public function add(){ if($this->request->isAjax()){ $post = $this->request->post(); $arr = []; - if(is_numeric($post['name'])) $this->error(lang('The plugin name cannot be a number')); + if(is_numeric($post['app'])) $this->error(lang('The plugin name cannot be a number')); foreach ($post as $k => $v) { if ($k == '__token__') continue; @@ -235,10 +228,10 @@ public function add(){ public function install(string $name='',string $type='') { set_time_limit(0); - $name = $this->request->param("name")??$name; - $plugins_id = $this->request->param("plugins_id"); - $version_id = $this->request->param("version_id"); - $type = $this->request->param("type")??$type; + $name = input("name")??$name; + $plugins_id = input("plugins_id",0); + $version_id = input("version_id",0); + $type = input("type")??$type; // 插件名是否为空 if (!$name) { $this->error(lang('addon %s can not be empty', [$name])); @@ -269,7 +262,7 @@ public function localinstall() if($file && file_exists('.'.$file)){ try { $res = ZipHelper::unzip('.'.$file,'../addons'); - }catch (\Exception $e){ + }catch (Exception $e){ $this->error($e->getMessage()); } if($res){ @@ -283,93 +276,38 @@ public function localinstall() } /** * @NodeAnnotation(title="卸载") - * @throws \think\db\exception\DataNotFoundException - * @throws \think\db\exception\DbException - * @throws \think\db\exception\ModelNotFoundException + * @throws \think\dbException\DataNotFoundException + * @throws \think\dbException\DbException + * @throws \think\dbException\ModelNotFoundException */ public function uninstall() { set_time_limit(0); - $name = $this->request->param("name"); - if (!$name) { - $this->error(lang(' addon name can not be empty')); - } - //插件名匹配 - if (!preg_match("/^[a-zA-Z0-9]+$/", $name)) { - $this->error(lang('addon name is not right')); - } - //获取插件信息 - $info = $this->modelClass->withTrashed()->where('name', $name)->find(); - if (empty($info)) { - $this->error(lang('addon is not exist')); - } - if($info->status==1){ - $this->error(lang('Please disable addons %s first',[$name])); - } - if (!$info->delete(true)) { - $this->error(lang('addon uninstall fail')); - } - //卸载插件 - $class = get_addons_instance($name); - $class->uninstall(); - //删除菜单 - $menu_config=get_addons_menu($name); + $name = input("name"); try { - if(!empty($menu_config)){ - list($menu,$pid) = $this->getMenu($menu_config); - $this->addonService->delAddonMenu($menu,$name); - } - //卸载sql; - uninstallsql($name); + $this->addonService->uninstallAddon($name); }catch (Exception $e){ $this->error($e->getMessage()); } - //还原文件 - Service::removeApp($name,$delete= true); - Service::updateAddonsInfo($name,1,0); - try { - //刷洗addon文件和配置 - refreshaddons(); - }catch (\Exception $e){ - $this->error($e->getMessage()); - } $this->success(lang('Uninstall successful')); } /** * @NodeAnnotation (title="禁用启用") - * @throws \think\db\exception\DataNotFoundException - * @throws \think\db\exception\DbException - * @throws \think\db\exception\ModelNotFoundException + * @throws \think\dbException\DataNotFoundException + * @throws \think\dbException\DbException + * @throws \think\dbException\ModelNotFoundException */ public function modify() { - $name = $this->request->param("name"); + $name = input("name"); if (!preg_match("/^[a-zA-Z0-9]+$/", $name)) { $this->error(lang('addon name is not right')); } - $info = $this->modelClass->where('name',$name)->find(); - $addoninfo = get_addons_info($name); - $addoninfo['status'] = $addoninfo['status']?0:1; try { - $info->status =$addoninfo['status']; - Service::updateAddonsInfo($name,$addoninfo['status']); - // 安装菜单 - $class = get_addons_instance($name); - $menu_config = get_addons_menu($name); - if(!empty($menu_config)){ - list($menu,$pid) = $this->getMenu($menu_config); - if( $addoninfo['status']){ - $this->addonService->addAddonMenu($menu,$pid,$name); - }else{ - $this->addonService->delAddonMenu($menu,$name); - } - } - refreshaddons(); - $info->save(); - $addoninfo['status']==1 ?$class->enabled():$class->disabled(); - }catch (\Exception $e){ - $this->error(lang($e->getMessage())); + $this->addonService->modifyAddon($name); + } catch (\Exception $e) { + $this->error($e->getMessage()); } $this->success(lang('operation success')); } @@ -378,9 +316,9 @@ public function modify() * @NodeAnnotation (title="插件配置") * @return \think\response\View * @throws Exception - * @throws \think\db\exception\DataNotFoundException - * @throws \think\db\exception\DbException - * @throws \think\db\exception\ModelNotFoundException + * @throws \think\dbException\DataNotFoundException + * @throws \think\dbException\DbException + * @throws \think\dbException\ModelNotFoundException */ public function config() { @@ -389,7 +327,7 @@ public function config() $one = $this->modelClass->find($id); $config = get_addons_config($name); if ($this->request->isAjax()) { - $params = $this->request->param('params/a',[],'trim'); + $params = input('params/a',[],'trim'); if ($params) { foreach ($config as $k => &$v) { if (isset($params[$k])) { @@ -425,6 +363,9 @@ public function config() $class->config(); } set_addons_config($name,$config); + if(!empty($config['app_rewrite']['content'])) { + set_app_route($name, $config['app_rewrite']['content']); + } refreshaddons(); $this->success(lang('operation success')); }else{ @@ -443,7 +384,7 @@ public function config() $this->error(lang('addon config is not found')); } //模板引擎初始化 - $view = ['formData'=>$config,'title'=>$one->name]; + $view = ['formData'=>$config,'title'=>$one['name']]; $configFile = app()->getRootPath() . 'addons' . DS . $name . DS . 'config.html'; $viewFile = file_exists($configFile) ? $configFile : ''; //重新加载引擎 @@ -451,22 +392,7 @@ public function config() return view($viewFile,$view); } - /** - * @NodeAnnotation (title="是否安装") - * @param $name - * @return array|false|\think\Model|null - * @throws \think\db\exception\DataNotFoundException - * @throws \think\db\exception\DbException - * @throws \think\db\exception\ModelNotFoundException - */ - public function isInstall($name) - { - if (empty($name)) { - return false; - } - $addons = $this->modelClass->withTrashed()->where('name', $name)->find(); - return $addons; - } + /** * 获取插件列表 @@ -486,13 +412,13 @@ protected function getLocalAddons(){ * @param string $type * @return true * @throws Exception - * @throws \think\db\exception\DataNotFoundException - * @throws \think\db\exception\DbException - * @throws \think\db\exception\ModelNotFoundException + * @throws \think\dbException\DataNotFoundException + * @throws \think\dbException\DbException + * @throws \think\dbException\ModelNotFoundException */ protected function doInstall(string $name,int $plugins_id=0,int $version_id=0,string $type=''){ //检查插件是否安装 - $list = $this->isInstall($name); + $list = $this->addonService->isInstall($name); if ($list && $list->status==1) { $this->error(lang('addons %s is already installed', [$name])); } @@ -500,65 +426,26 @@ protected function doInstall(string $name,int $plugins_id=0,int $version_id=0,st //本地存在空和更新则请求后端 if(empty($type) || $type=='upgrade'){ //不存在或者 - $postData = $this->getCloundData($name,$plugins_id,$version_id); + $postData = $this->getCloudData($name,$plugins_id,$version_id); if(!$localNameArr || !in_array($name,$localNameArr) || !isset($addons[$name]) ){ - $this->getCloundAddons($postData); + try { + $this->getCloudAddons($postData); + } catch (Exception $e) { + $this->error($e->getMessage()); + } } if($type =='upgrade'){ - $this->getCloundAddons($postData); - } - } - $class = get_addons_instance($name); - if (empty($class)) { - $this->error(lang('addons %s is not ready', [$name])); - } - $addon_info = get_addons_info($name); - if(!empty($addon_info['depend'])){ - $depend = explode(',',$addon_info['depend']); - foreach ($depend as $v) { - $dependAddon = get_addons_info($v); - if(empty($dependAddon)){ - $this->error('Please install the dependent plugin first: '.$addon_info['depend']); + try { + $this->getCloudAddons($postData); + } catch (Exception $e) { + $this->error($e->getMessage()); } } } - //添加数据库 - try{ - if($type!='upgrade'){ - importsql($name); - } - } catch (Exception $e){ - $this->error($e->getMessage()); - } - - // 安装菜单 - $menu_config=get_addons_menu($name); - if(!empty($menu_config)){ - list($menu,$pid) = $this->getMenu($menu_config); - $this->addonService->addAddonMenu($menu,$pid,$name); - } - - //安装插件 - $class->install(); - $addon_info['status'] = 1; - if($list){ - if($list->delete_time > 0){ - $this->modelClass->restore(['id'=>$list->id]); - } - $res = $this->modelClass->update(['status'=>1],['id'=>$list->id]); - }else{ - $res = $this->modelClass->save($addon_info); - } - if (!$res) { - $this->error(lang('addon install fail')); - } - Service::copyApp($name,$delete = true); try { - Service::updateAddonsInfo($name); - //刷新addon文件 - refreshaddons(); - }catch (\Exception $e){ + $this->addonService->installAddon($name,$type); + } catch (Exception $e) { $this->error($e->getMessage()); } Cache::clear(); @@ -567,14 +454,14 @@ protected function doInstall(string $name,int $plugins_id=0,int $version_id=0,st /** * 更新 先卸载插件 * @return bool - * @throws \think\db\exception\DataNotFoundException - * @throws \think\db\exception\DbException - * @throws \think\db\exception\ModelNotFoundException + * @throws \think\dbException\DataNotFoundException + * @throws \think\dbException\DbException + * @throws \think\dbException\ModelNotFoundException */ protected function doUpgrade(string $name='') { set_time_limit(0); - $name = $name?:$this->request->param("name"); + $name = $name?:input("name"); //获取插件信息 $info = $this->modelClass->withTrashed()->where('name', $name)->find(); if($info && $info->status==1){ @@ -590,7 +477,7 @@ protected function doUpgrade(string $name='') $menu_config=get_addons_menu($name); try { if(!empty($menu_config)){ - list($menu,$pid) = $this->getMenu($menu_config); + list($menu,$pid) = $this->addonService->getMenu($menu_config); $this->addonService->delAddonMenu($menu,$name); } //为了防止文件误删,这里先不卸载sql @@ -602,30 +489,40 @@ protected function doUpgrade(string $name='') if(file_exists($sql)){ importSqlData($sql); } + $sql = root_path().'addons/'.$name.'/'.'update.sql'; + if(file_exists($sql)){ + importSqlData($sql); + } Service::updateAddonsInfo($name,1,0); try { //刷新addon文件和配置 refreshaddons(); - }catch (\Exception $e){ + }catch (Exception $e){ $this->error($e->getMessage()); } return true; } /** - * 获取远程安装包 - * @param $params + * @param array $params * @return void - * @throws \Exception + * @throws Exception */ - protected function getCloundAddons(array $params=[]){ - $res = $this->authCloudService->setApiUrl('api/v1.plugins/down')->setMethod('GET') - ->setParams($params)->setHeader()->setOptions()->run(); - if($res['code'] == 401){ - Cookie::delete('auth_account'); - $this->error(lang('please login aigin')); - } - if($res['code']!=200){ + protected function getCloudAddons(array $params=[]): void + { + $res = $this->authCloudService + ->setApiUrl('/api/v2.plugins/down') + ->setParams($params) + ->setHeader() + ->run(); + if(empty($res)){ + $this->error(lang('Api request error')); + } + if(isset($res['code']) && $res['code'] == 401){ + $this->authCloudService->setToken()->setMember(); + $this->error(lang('please login again')); + } + if(isset($res['code']) && $res['code']!=200){ $url = ''; if(!empty($res['data']['url'])) { $url = $res['data']['url']; @@ -658,7 +555,8 @@ protected function getCloundAddons(array $params=[]){ * @param $version_id * @return array */ - protected function getCloundData(string $name,int $plugins_id=0,int $version_id=0){ + protected function getCloudData(string $name,int $plugins_id=0,int $version_id=0): array + { return [ 'plugins_id'=>$plugins_id, 'name'=>$name, @@ -667,6 +565,7 @@ protected function getCloundData(string $name,int $plugins_id=0,int $version_id 'app_version'=>$this->app_version, "ip" => request()->ip(), "domain" => request()->domain(), + "access_token" => $this->authCloudService->getToken(), ]; } /** @@ -674,47 +573,8 @@ protected function getCloundData(string $name,int $plugins_id=0,int $version_id * @return void */ public function logout(){ - Cookie::delete('auth_account'); - Cookie::delete('clound_account'); + $this->authCloudService->setToken()->setMember(); $this->success(lang('logout success')); } - - /** - * 获取菜单 - * @param $menu - * @return void - */ - protected function getMenu($config = []) - { - $is_nav = $config['is_nav']??1; - $menuArr = $config['menu']; - $menu = []; - if(!empty($menuArr[0]) && is_array($menuArr[0])){ - foreach ($menuArr as $value) { - if($is_nav==-1){ - $menu = array_merge($menu,$value['menulist']); - $pid = 0; - }elseif($is_nav==0){ - $menu[] = $value; - $pid = $this->addonService->addAddonManager()->id; - }else{ - $menu[] = $value; - $pid = 0; - } - } - }else{ - if($is_nav==-1){ - $menu = array_merge($menu,$menuArr['menulist']); - $pid = 0; - }elseif($is_nav==0){ - $menu[] = $menuArr; - $pid = $this->addonService->addAddonManager()->id; - }else{ - $menu[] = $menuArr; - $pid = 0; - } - } - return [$menu,$pid]; - } } diff --git a/app/backend/controller/Ajax.php b/app/backend/controller/Ajax.php index 1fd3146d1..ac39e695b 100644 --- a/app/backend/controller/Ajax.php +++ b/app/backend/controller/Ajax.php @@ -13,7 +13,6 @@ namespace app\backend\controller; -use app\backend\middleware\CheckRole; use app\backend\middleware\SystemLog; use app\backend\middleware\ViewNode; use app\backend\model\AuthRule; @@ -29,21 +28,16 @@ class Ajax extends Backend { - protected $middleware = [ - CheckRole::class=>['only'=>[]], - ViewNode::class, - SystemLog::class - ]; + + protected array $onlyNeedLogin = ['uploads']; public function __construct(App $app) { $this->modelClass = new AttachModel(); parent::__construct($app); } + /** * @return \think\response\Json - * @throws \think\db\exception\DataNotFoundException - * @throws \think\db\exception\DbException - * @throws \think\db\exception\ModelNotFoundException * 文件上传总入口 集成qiniu ali tenxunoss */ public function uploads() @@ -52,7 +46,7 @@ public function uploads() $upload = UploadService::instance(); $result = $upload->uploads(0,session('admin.id')); return json($result); - } catch (Exception $e) { + } catch (\Exception $e) { $this->error($e->getMessage()); } } @@ -69,8 +63,8 @@ public function refreshmenu() ->where('type',1) ->order('sort asc') ->select()->toArray(); - $menulsit = AuthService::instance()->menuhtml($cate); - $this->success('ok','',$menulsit); + $menuList = AuthService::instance()->menuhtml($cate); + $this->success('ok','',$menuList); } /** * @return \think\response\Jsonp diff --git a/app/backend/controller/Index.php b/app/backend/controller/Index.php index 86487a63e..6479cf459 100644 --- a/app/backend/controller/Index.php +++ b/app/backend/controller/Index.php @@ -13,7 +13,6 @@ namespace app\backend\controller; -use app\backend\middleware\CheckRole; use app\backend\middleware\SystemLog; use app\backend\middleware\ViewNode; use app\backend\model\AuthRule; @@ -29,11 +28,9 @@ class Index extends Backend { - protected $middleware = [ - CheckRole::class=>['except'=>['console','logout']], - ViewNode::class, - SystemLog::class - ]; + + protected array $noNeedLogin = ['console','logout']; + protected $layout = ''; /** * @return string diff --git a/app/backend/controller/Login.php b/app/backend/controller/Login.php index 9fc175465..576887b11 100644 --- a/app/backend/controller/Login.php +++ b/app/backend/controller/Login.php @@ -11,35 +11,36 @@ * Date: 2017/8/2 */ namespace app\backend\controller; -use app\backend\middleware\CheckRole; use app\backend\middleware\SystemLog; use app\backend\middleware\ViewNode; use app\backend\service\AuthService; use app\common\controller\Backend; use Exception; use fun\helper\SignHelper; +use http\Header; use think\App; class Login extends Backend { + + + protected array $noNeedLogin = ['index','verify']; + protected $layout=''; - protected $middleware = [ - CheckRole::class=>['except'=>['index','verify']], - ViewNode::class, - SystemLog::class - ]; - public function __construct(App $app) { + public function __construct(App $app) { parent::__construct($app); + if(config('funadmin.standalone') && request()->baseFile()=='/index.php'){ + header("Location: /"); + exit(); + } } public function index(){ if (!$this->request->isPost()) { $admin= session('admin'); - $admin_sign= session('admin.token') == SignHelper::authSign($admin) ? $admin['id'] : 0; - // 签名验证是否存在 - if ($admin && $admin_sign) { + if ($admin) { $this->redirect(__u('index/index')); } - $bg = hook('bgHook')?hook('bgHook'):'/static/backend/images/admin-bg.jpg'; + $bg = hook_one('bgHook')?hook_one('bgHook'):'/static/backend/images/admin-bg.jpg'; $view = ['bg'=>$bg]; return view('',$view); } else { @@ -62,7 +63,7 @@ public function index(){ } catch (Exception $e) { $this->error(lang('Login Failed').":{$e->getMessage()}"); } - $this->success(lang('Login Success').'...',__u('index/index')); + $this->success(lang('Login Success').'...',__u('/index/index')); } } public function verify() diff --git a/app/backend/controller/auth/Admin.php b/app/backend/controller/auth/Admin.php index c3902a0f5..ac6e1a437 100644 --- a/app/backend/controller/auth/Admin.php +++ b/app/backend/controller/auth/Admin.php @@ -164,8 +164,8 @@ public function upme() $this->error(lang('operation failed')); } } - $list = $this->modelClass->find($id); - $list->password = ''; + $list = $this->modelClass->find($id)->toArray(); + $list['password'] = ''; $auth_group = AuthGroupModel::where('status', 1)->select(); if($list['group_id']) $list['group_id'] = explode(',',$list['group_id']); $view = [ @@ -190,7 +190,7 @@ public function edit() $id = $this->request->param('id'); if ($this->request->isPost()) { $post = $this->request->post(); - $rule = ['group_id'=>'require']; + $rule = ['group_id'=>'require','username'=>'require','realname'=>'require']; $this->validate($post, $rule); if(session('admin.id')) if($post['password']){ @@ -206,9 +206,9 @@ public function edit() $this->error(lang('operation failed')); } } - $list = $this->modelClass->find($id); + $list = $this->modelClass->find($id)->toArray(); if($list['group_id']) $list['group_id'] = explode(',',$list['group_id']); - $list->password = ''; + $list['password'] = ''; $authGroup = $this->getAuthGroup(); $view = [ 'formData' =>$list, diff --git a/app/backend/controller/auth/AuthGroup.php b/app/backend/controller/auth/AuthGroup.php index 1125d4be0..af065ba16 100644 --- a/app/backend/controller/auth/AuthGroup.php +++ b/app/backend/controller/auth/AuthGroup.php @@ -131,6 +131,9 @@ public function edit() if($id==1){ $this->error(lang('SupperAdmin cannot edit')); } + if($post['pid']==$id){ + $this->error(lang('Superiors can not be for themselves')); + } $res = $list->save($post); if($res){ $this->success(lang('operation success')); @@ -142,10 +145,11 @@ public function edit() $id = $this->request->param('id'); $list = $this->modelClass->find(['id' => $id]); $where = []; - if(session('admin.id')!==1){ - $where[] = ['id','in',session('admin.group_id')]; - } - $authGroup = $this->modelClass->where('status',1)->where($where)->select()->toArray(); +// if(session('admin.id')!=1){ +// $where[] = ['id','in',session('admin.group_id')]; +// } + $authGroup = $this->modelClass->where('status',1)->where($where) + ->where('id','<>',$id)->select()->toArray(); foreach ($authGroup as $key=>$item) { $parent = $this->modelClass->where($where)->where('id',$item['pid'])->find(); if(empty($parent)){ diff --git a/app/backend/controller/member/Member.php b/app/backend/controller/member/Member.php index 91d06f7b2..a53f69db6 100644 --- a/app/backend/controller/member/Member.php +++ b/app/backend/controller/member/Member.php @@ -39,8 +39,16 @@ public function __construct(App $app) public function getcitys(){ if($this->request->isAjax()) {}{ $pid = $this->request->param('pid',0); - $citys = Provinces::where('pid',$pid)->order('id desc')->field('id,name,pid') - ->cache('provinces.'.$pid,3600*24)->select()->toArray(); + $pid = $this->request->param('province_id',0); + $city_id = $this->request->param('city_id'); + if($city_id){ + $citys = Provinces::where('pid',$city_id)->order('id desc')->field('id,name,pid') + ->cache('provinces.'.$city_id,3600*24)->select()->toArray(); + }else{ + $citys = Provinces::where('pid',$pid)->order('id desc')->field('id,name,pid') + ->cache('provinces.'.$pid,3600*24)->select()->toArray(); + } + $this->success('','',$citys); } } @@ -59,7 +67,7 @@ public function index() } list($this->page, $this->pageSize, $sort, $where) = $this->buildParames(); $list = $this->modelClass - ->withJoin(['memberGroup','memberLevel']) + ->withJoin(['group','level']) ->where($where) ->order($sort) ->paginate([ @@ -227,11 +235,11 @@ public function recycle() if ($this->request->isAjax()) { list($this->page, $this->pageSize, $sort, $where) = $this->buildParames(); $count = $this->modelClass->onlyTrashed() - ->withJoin(['memberGroup','memberLevel']) + ->withJoin(['group','level']) ->where($where) ->count(); $list = $this->modelClass->onlyTrashed() - ->withJoin(['memberGroup','memberLevel']) + ->withJoin(['group','level']) ->where($where) ->order($sort) ->page($this->page, $this->pageSize) diff --git a/app/backend/controller/sys/Upgrade.php b/app/backend/controller/sys/Upgrade.php index 89f9eeec5..b0a7e5ed6 100644 --- a/app/backend/controller/sys/Upgrade.php +++ b/app/backend/controller/sys/Upgrade.php @@ -55,17 +55,18 @@ public function __construct(App $app) public function index() { if ($this->request->isPost()) { - $data = $this->request->post(); - $this->authCloudService->setUserParams($data); - $result = $this->authCloudService->setApiUrl('')->setMethod('post') - ->setParams($this->authCloudService->getUserParams()) - ->run(); - if ($result['code'] == 200) { - $this->authCloudService->setAuth($result['data']); - $this->success(lang('login successful')); - } else { - $this->error(lang('Login failed:' . $result['msg'])); + try { + $data = $this->request->post(); + // 获取访问令牌 + $tokenResult = $this->authCloudService->getAccessToken($data); + // 获取用户信息 + $member = $this->authCloudService->getMemberInfo($tokenResult['access_token']); + // 设置用户信息并返回成功 + $this->authCloudService->setMember($member); + } catch (Exception $e) { + $this->error(lang('Login failed:' . $e->getMessage())); } + $this->success( lang( 'login successful'),'',$member); } $data['now_version'] = $this->now_version; $version = Db::query('SELECT VERSION() AS ver'); @@ -80,7 +81,7 @@ public function index() 'server_file' => $_SERVER['SCRIPT_FILENAME'], 'php_version' => PHP_VERSION, 'mysql_version' => $version[0]['ver'], - 'auth' => $this->authCloudService->getAuth() ? 1 : 0, + 'auth' => $this->authCloudService->getMember() ? 1 : 0, ]; $view = array_merge($data, $view); return view('', $view); @@ -93,7 +94,7 @@ public function index() */ public function check() { - if (!$this->authCloudService->getAuth()) { + if (!$this->authCloudService->getMember()) { $this->error(lang('请先登录FunAdmin系统')); } $params = [ @@ -101,7 +102,7 @@ public function check() "domain" => request()->domain(), "version" => $this->now_version, ]; - $result = $this->authCloudService->setApiUrl('api/v1.version/getVersion') + $result = $this->authCloudService->setApiUrl('api/v2.version/getVersion') ->setParams($params)->setHeader()->run(); if ($result['code'] == 200) { session('upgradeInfo', $result['data']); @@ -118,7 +119,7 @@ public function check() public function backup() { if ($this->request->isPost()) { - if (!$this->authCloudService->getAuth()) { + if (!$this->authCloudService->getMember()) { $this->error(lang('请先登录FunAdmin系统')); } if (!is_dir($this->backup_dir)) { @@ -141,7 +142,7 @@ public function backup() public function install() { if ($this->request->isPost()) { - if (!$this->authCloudService->getAuth()) { + if (!$this->authCloudService->getMember()) { $this->error(lang('请先登录FunAdmin系统')); } if(!file_exists($this->lockFile)){ diff --git a/app/backend/lang/zh-cn.php b/app/backend/lang/zh-cn.php index 5d79fb921..721b03be4 100644 --- a/app/backend/lang/zh-cn.php +++ b/app/backend/lang/zh-cn.php @@ -13,253 +13,245 @@ */ return [ - 'Home' => '主页', - 'UserName or Email' => '用户名或邮箱', - 'password' => '密码', - 'Login In' => '登入', - 'Login' => '登入', - 'Register' => '注册', - 'Lang' => '语言', - 'language' => '语言', - 'Input' => '输入', - 'Top' => "顶级", - 'Pid' => '上级id', - 'Oprate' => '操作', - 'Edit' => '编辑', - 'Status' => '状态', - 'Del' => '销毁', - 'delete' => '删除', - 'destroy' => '伪删除', - 'Destory' => '伪删除', - 'Restore' => '还原', - 'recycle' => '回收站', - 'Import' => '导入', - 'Export' => '导出', - 'Add' => '添加', - 'Update' => '更新', - 'Operat' => '操作', - 'List' => '列表', - 'Modify' => '操作', - 'Copy' => '复制', - 'Submit' => '提交', - 'Access' => '分配', - 'AddChild' => '添加子类', - 'Back' => '返回', - 'Confirm' => '确定', - 'Cancel' => '取消', - 'Remark' => '备注', - 'Reset' => '重置', - 'Open' => '开启', - 'Close' => '关闭', - 'Email' => '邮箱', - 'Ip' => 'ip地址', - 'Yes' => '是', - 'No' => '否', - 'All' => '全部', - 'MOVE' => '移动', - 'SelectFiles' =>'选择文件', - 'Mobile' => '手机号', - 'PleaseEnter' => '请输入', - 'Username' => '用户名', - 'Password' => '密码', - 'Admin' => '管理员', - 'Admin Id' => '管理员id', - 'Search' => '搜索', - 'User' => '用户', - 'Sex' => '性别', - 'Level' => '等级', - 'Please choose data' => '请选择数据', - 'Defaults' => '默认', - 'Default' => '默认', - 'Cropper' =>'裁剪', - 'IMAGE SIZE LIMITED 300x300px,MAXSIZE 2M'=>'文件大小限制在300X300 2M内', - 'Choose' =>'选择', - 'upgrade' =>'更新', - 'check' =>'检测', - 'backup' =>'备份', - 'Wechat' => '微信', - 'Message' => '消息', - 'Material' => '素材', - 'Tag' => '标签', - 'Tags' => '标签', - 'Fans' => '粉丝', - 'Reply' => '回复', - 'Supperman' => '超级管理', - 'Theme Color' =>'主题颜色', - 'Sort' => '排序', - 'Field' => '字段', - 'Name' => '名字', - 'Value' => '值', - 'Config' => '配置', - 'Plugins' => '插件', - "File" => "文件", - "debug" => '调试模式', - "version" => '版本', - "other" => '其他', - 'Upload' => "上传", - 'Refresh Success' => '刷新成功', - 'Refresh' => '刷新页面', - 'Description' => "描述", - 'Set Password To Lock Screen' => '输入密码锁屏', - 'Lock now' => '立即锁屏', - 'Input Password' => '输入密码', - 'Unlock Now' => '立即解锁', - 'Unlock Success' => '解锁成功', - - 'Delete checked' => '批量删除', - 'operation success' => '操作成功', - 'operation failed' => '操作失败', - 'Update Success' => '更新成功', - 'Empty Success' => '清除成功', - 'Update Failed' => '更新失败', - 'Delete all Failed' => '全部删除失败', - 'Asny Failed' => '同步失败', - 'Empty Failed' => '清除失败', - 'enabled' =>'启用', - 'disabled' =>'禁用', - - 'Test Data Cannot Edit' => '测试数据不能修改', - 'Test Data Cannot Delete' => '测试数据不能删除', - 'Please Choose Rule' => '请选择权限', - - 'Not exist' => '不存在', - 'Data Not Exist' => '数据不存在', - 'Cannot Null' => '不能为空', - 'Invalid Data' => '数据不正确', - 'Invalid Options' => '非法操作', - - 'Logout Success' => '退出成功', - 'QrcodeName' => "二维码名字", - 'Select Attach' => '选择附件', - 'CreateTime' => '创建时间', - 'UpdateTime' => '更新时间', - 'Verify' => '验证码', - 'Please check data' => '请检查数据', - 'Are you sure' => '确定操作吗?', - 'Are you sure you want to delete it' => '确定删除此项?', - 'Are you sure to delete' => '确定删除此项?', - 'Are you sure to Destroy' => '确定删除此项?', - 'Are you sure to restore' => '确定还原吗?', + 'Home' => '主页', + 'UserName or Email' => '用户名或邮箱', + 'password' => '密码', + 'Login In' => '登入', + 'Login' => '登入', + 'Register' => '注册', + 'Lang' => '语言', + 'language' => '语言', + 'Input' => '输入', + 'Top' => "顶级", + 'Pid' => '上级id', + 'Oprate' => '操作', + 'Edit' => '编辑', + 'Status' => '状态', + 'Del' => '销毁', + 'delete' => '删除', + 'destroy' => '伪删除', + 'Destory' => '伪删除', + 'Restore' => '还原', + 'recycle' => '回收站', + 'Import' => '导入', + 'Export' => '导出', + 'Add' => '添加', + 'Update' => '更新', + 'Operat' => '操作', + 'List' => '列表', + 'Modify' => '操作', + 'Copy' => '复制', + 'Submit' => '提交', + 'Access' => '分配', + 'AddChild' => '添加子类', + 'Back' => '返回', + 'Confirm' => '确定', + 'Cancel' => '取消', + 'Remark' => '备注', + 'Reset' => '重置', + 'Open' => '开启', + 'Close' => '关闭', + 'Email' => '邮箱', + 'Ip' => 'ip地址', + 'Yes' => '是', + 'No' => '否', + 'All' => '全部', + 'MOVE' => '移动', + 'SelectFiles' => '选择文件', + 'Mobile' => '手机号', + 'PleaseEnter' => '请输入', + 'Username' => '用户名', + 'Password' => '密码', + 'Admin' => '管理员', + 'Admin Id' => '管理员id', + 'Search' => '搜索', + 'User' => '用户', + 'Sex' => '性别', + 'Level' => '等级', + 'Please choose data' => '请选择数据', + 'Defaults' => '默认', + 'Default' => '默认', + 'Cropper' => '裁剪', + 'IMAGE SIZE LIMITED 300x300px,MAXSIZE 2M' => '文件大小限制在300X300 2M内', + 'Choose' => '选择', + 'upgrade' => '更新', + 'check' => '检测', + 'backup' => '备份', + 'Wechat' => '微信', + 'Message' => '消息', + 'Material' => '素材', + 'Tag' => '标签', + 'Tags' => '标签', + 'Fans' => '粉丝', + 'Reply' => '回复', + 'Supperman' => '超级管理', + 'Theme Color' => '主题颜色', + 'Sort' => '排序', + 'Field' => '字段', + 'Name' => '名字', + 'Value' => '值', + 'Config' => '配置', + 'Plugins' => '插件', + "File" => "文件", + "debug" => '调试模式', + "version" => '版本', + "other" => '其他', + 'Upload' => "上传", + 'Refresh Success' => '刷新成功', + 'Refresh' => '刷新页面', + 'Description' => "描述", + 'Set Password To Lock Screen' => '输入密码锁屏', + 'Lock now' => '立即锁屏', + 'Input Password' => '输入密码', + 'Unlock Now' => '立即解锁', + 'Unlock Success' => '解锁成功', + 'Delete checked' => '批量删除', + 'operation success' => '操作成功', + 'operation failed' => '操作失败', + 'Update Success' => '更新成功', + 'Empty Success' => '清除成功', + 'Update Failed' => '更新失败', + 'Delete all Failed' => '全部删除失败', + 'Asny Failed' => '同步失败', + 'Empty Failed' => '清除失败', + 'enabled' => '启用', + 'disabled' => '禁用', + 'Test Data Cannot Edit' => '测试数据不能修改', + 'Test Data Cannot Delete' => '测试数据不能删除', + 'Please Choose Rule' => '请选择权限', + 'Not exist' => '不存在', + 'Data Not Exist' => '数据不存在', + 'Cannot Null' => '不能为空', + 'Invalid Data' => '数据不正确', + 'Invalid Options' => '非法操作', + 'Logout Success' => '退出成功', + 'QrcodeName' => "二维码名字", + 'Select Attach' => '选择附件', + 'CreateTime' => '创建时间', + 'UpdateTime' => '更新时间', + 'Verify' => '验证码', + 'Please check data' => '请检查数据', + 'Are you sure' => '确定操作吗?', + 'Are you sure you want to delete it' => '确定删除此项?', + 'Are you sure to delete' => '确定删除此项?', + 'Are you sure to Destroy' => '确定删除此项?', + 'Are you sure to restore' => '确定还原吗?', 'Are you sure you want to delete the %s selected item?' => '确定删除选中的 %s 项?', - 'Are you sure you want to delete or empty?' => '确定删除或清空?', - 'Are you sure you want to empty?' => '确定清空?', + 'Are you sure you want to delete or empty?' => '确定删除或清空?', + 'Are you sure you want to empty?' => '确定清空?', 'English characters Canot duplicate the current existing' => '英文字符,不能重复', - 'Please install the upload plugin first'=>'请先安装上传插件', - + 'Please install the upload plugin first' => '请先安装上传插件', //菜单 - 'FileList' =>'文件列表', - 'groupadd' =>'分组添加', - 'groupedit' =>'分组编辑', - 'groupdelete' =>'分组删除', - 'Fullscreen' => "全屏", - 'Lock' => "锁屏", - 'zh-cn' => "中文", - 'en-us' => "英文", - 'Theme' => "主题", - 'Sys' => "系统", - 'System' => "系统", - 'Log' => "日志", - 'LogDel' => "日志删除", - 'Uploads' => "上传", - 'Group' => "组别", - 'ConfigGroup' => "配置组", - 'ConfigGroupAdd' => "配置组添加", - 'ConfigGroupEdit' => "配置组编辑", - 'ConfigGroupDel' => "配置组删除", - 'ConfigGroupModify' => "配置组修改", - 'ConfigEdit' => "配置修改", - 'ConfigAdd' => "配置添加", - 'ConfigDel' => "配置删除", - 'ConfigModify' => "配置编辑", - 'ConfigSet' => "配置设置", - 'Blacklist' => "黑名单", - 'AuthBoard' => "权限", - 'Auth' => "菜单", - 'AuthGroup' => "角色组", - 'AuthGroupAdd' => "角色组添加", - 'AuthGroupEdit' => "角色组编辑", - 'AuthGroupModify' => "角色组修改", - 'AuthGroupDel' => "角色组删除", - 'AuthGroupAccess' => "角色组配置", - 'Rule' => "规则", - 'RuleAdd' => "添加规则", - 'RuleDel' => "删除规则", - 'RuleEdit' => "编辑规则", - 'RuleModify' => "修改规则", - 'Add ChildRule' => "添加子规则", - 'AttachGroup' => "附件分组", - 'attachGroup' => "附件分组", - 'Attach' => "附件", - 'AttachAdd' => "附件", - 'AttachDelete' => "附件", - 'Ucenter' => '会员中心', - 'UserLevel' => '用户等级', - 'Member' => "会员 ", - 'MemberAdd' => "会员添加", - 'MemberEdit' => "会员编辑", - 'MemberDel' => "会员删除", - 'MemberModify' => "会员修改", - 'MemberLevel' => "会员等级", - 'MemberLevelAdd' => "会员等级添加", - 'MemberLevelEdit' => "会员等级编辑", - 'MemberLevelDel' => "会员等级删除", - 'MemberLevelModify' => "会员等级修改", - 'MemberGroup' => "会员组", - 'MemberGroupADD' => "会员组添加", - 'MemberGroupEdit' => "会员组编辑", - 'MemberGroupDel' => "会员组删除", - 'MemberGroupModify' => "会员组修改", - 'AdminAdd' => "管理员添加", - 'AdminEdit' => "管理员编辑", - 'AdminModify' => "管理员修改", - 'AdminDel' => "管理员删除", - 'Child' => '下级', - 'Attachment' => '附件', - 'Addons' => '插件', - 'Addon' => '插件', - 'AddonName' => '插件名', - 'Uninstall' => '卸载', - 'Install' => '安装', - 'Languages' => '语言', - 'Sysset' => '设置', - 'clear all' =>"清除全部缓存", - 'clear frontend' =>"清除前台缓存", - 'clear backend' =>"清除后台缓存", - 'Please Login First' => '请先登录', - 'Login successful' => '登录成功', - 'Are you sure todo this' =>'确定吗?', - - "Demo is not allow to change data"=>"演示站点不允许修改数据", - 'safe' =>'安全设置', - 'info' =>'基本信息', - 'Logout' =>'退出', - 'Try again later' =>'稍后再试', - 'Upload Success' =>'上传成功', - 'Upload Failed' =>'上传失败', - 'uploading' =>'上传中', - 'File nums is limited' =>'文件数量有限', - 'Are you sure?' =>'确定吗?', - 'Request url can not empty' =>'请求网址不能为空', - 'Choose Success' =>'选择成功', - 'Return data is not right' =>'返回数据不正确', - 'File type is limited' =>"不支持上传文件类型,请配置", - - 'OriginalName' =>'原名', - 'Path' =>'路径', - 'MimeType' =>'Mime类型', - 'Permission Denied' =>'没有权限', - 'Between 4 and 25 characters'=>'4-25个字符', - - - "save success"=>'保存成功', - "add success"=>'保存成功', - "add failed"=>'保存失败', - "operate success"=>'操作成功', - "operate failed"=>'操作失败', - "save failed"=>'保存失败', - "modify success"=>'修改成功', - "modify failed"=>'修改失败', - 'are you sure you want to close this window'=>"确定关闭窗口吗?", - 'key'=>'键', - 'Query'=>'参数' + 'FileList' => '文件列表', + 'groupadd' => '分组添加', + 'groupedit' => '分组编辑', + 'groupdelete' => '分组删除', + 'Fullscreen' => "全屏", + 'Lock' => "锁屏", + 'zh-cn' => "中文", + 'en-us' => "英文", + 'Theme' => "主题", + 'Sys' => "系统", + 'System' => "系统", + 'Log' => "日志", + 'LogDel' => "日志删除", + 'Uploads' => "上传", + 'Group' => "组别", + 'ConfigGroup' => "配置组", + 'ConfigGroupAdd' => "配置组添加", + 'ConfigGroupEdit' => "配置组编辑", + 'ConfigGroupDel' => "配置组删除", + 'ConfigGroupModify' => "配置组修改", + 'ConfigEdit' => "配置修改", + 'ConfigAdd' => "配置添加", + 'ConfigDel' => "配置删除", + 'ConfigModify' => "配置编辑", + 'ConfigSet' => "配置设置", + 'Blacklist' => "黑名单", + 'AuthBoard' => "权限", + 'Auth' => "菜单", + 'AuthGroup' => "角色组", + 'AuthGroupAdd' => "角色组添加", + 'AuthGroupEdit' => "角色组编辑", + 'AuthGroupModify' => "角色组修改", + 'AuthGroupDel' => "角色组删除", + 'AuthGroupAccess' => "角色组配置", + 'Rule' => "规则", + 'RuleAdd' => "添加规则", + 'RuleDel' => "删除规则", + 'RuleEdit' => "编辑规则", + 'RuleModify' => "修改规则", + 'Add ChildRule' => "添加子规则", + 'AttachGroup' => "附件分组", + 'attachGroup' => "附件分组", + 'Attach' => "附件", + 'AttachAdd' => "附件", + 'AttachDelete' => "附件", + 'Ucenter' => '会员中心', + 'UserLevel' => '用户等级', + 'Member' => "会员 ", + 'MemberAdd' => "会员添加", + 'MemberEdit' => "会员编辑", + 'MemberDel' => "会员删除", + 'MemberModify' => "会员修改", + 'MemberLevel' => "会员等级", + 'MemberLevelAdd' => "会员等级添加", + 'MemberLevelEdit' => "会员等级编辑", + 'MemberLevelDel' => "会员等级删除", + 'MemberLevelModify' => "会员等级修改", + 'MemberGroup' => "会员组", + 'MemberGroupADD' => "会员组添加", + 'MemberGroupEdit' => "会员组编辑", + 'MemberGroupDel' => "会员组删除", + 'MemberGroupModify' => "会员组修改", + 'AdminAdd' => "管理员添加", + 'AdminEdit' => "管理员编辑", + 'AdminModify' => "管理员修改", + 'AdminDel' => "管理员删除", + 'Child' => '下级', + 'Attachment' => '附件', + 'Addons' => '插件', + 'Addon' => '插件', + 'AddonName' => '插件名', + 'Uninstall' => '卸载', + 'Install' => '安装', + 'Languages' => '语言', + 'Sysset' => '设置', + 'clear all' => "清除全部缓存", + 'clear frontend' => "清除前台缓存", + 'clear backend' => "清除后台缓存", + 'Please Login First' => '请先登录', + 'Login successful' => '登录成功', + 'Are you sure todo this' => '确定吗?', + "Demo is not allow to change data" => "演示站点不允许修改数据", + 'safe' => '安全设置', + 'info' => '基本信息', + 'Logout' => '退出', + 'Try again later' => '稍后再试', + 'Upload Success' => '上传成功', + 'Upload Failed' => '上传失败', + 'uploading' => '上传中', + 'File nums is limited' => '文件数量有限', + 'Are you sure?' => '确定吗?', + 'Request url can not empty' => '请求网址不能为空', + 'Choose Success' => '选择成功', + 'Return data is not right' => '返回数据不正确', + 'File type is limited' => "不支持上传文件类型,请配置", + 'OriginalName' => '原名', + 'Path' => '路径', + 'MimeType' => 'Mime类型', + 'Permission Denied' => '没有权限', + 'Between 4 and 25 characters' => '4-25个字符', + "save success" => '保存成功', + "add success" => '保存成功', + "add failed" => '保存失败', + "operate success" => '操作成功', + "operate failed" => '操作失败', + "save failed" => '保存失败', + "modify success" => '修改成功', + "modify failed" => '修改失败', + 'are you sure you want to close this window'=> "确定关闭窗口吗?", + 'key' => '键', + 'Query' => '参数', + 'The drop-down selection field is incorrect'=> '下拉选择字段错误', ]; \ No newline at end of file diff --git a/app/backend/lang/zh-cn/addon.php b/app/backend/lang/zh-cn/addon.php index 737d68a93..0dc93783a 100644 --- a/app/backend/lang/zh-cn/addon.php +++ b/app/backend/lang/zh-cn/addon.php @@ -1,48 +1,48 @@ '插件名字不正确', - 'addon can not be empty'=>'插件不能为空', - 'addon %s can not be empty'=>'插件 %s 不能为空', - 'addon config is not found'=>'找不到插件配置', - 'addons %s is already installed'=>'插件%s已安装', - 'addons %s is not ready'=>'插件%s还没有准备好', - 'Please disable addons %s first'=>'请先禁用插件%s', - 'addon uninstall fail'=>'插件卸载失败', - 'Uninstall successful'=>'插件卸载成功', - 'LOGO'=>'Logo', - 'Create'=>'创建', - 'title'=>'标题', - 'Account'=>'云账号', - 'Addon version'=>'版本', - 'Addon Config'=>'插件配置', - 'Addon require'=>'需求版本', - 'author'=>'作者', - 'publishtime'=>'发布时间', - 'localinstall'=>'离线安装', - 'local install'=>'离线安装', - 'price'=>'价格', - 'download'=>'下载量', - 'Logo'=>'图标', - 'ADDONAME'=>'插件名', - 'config'=>'配置', - 'install'=>'安装', - 'Ver'=>'版本', - 'Force'=>'强制', - 'Requires'=>'需求版本', - 'uninstall'=>'卸载', - 'disabled'=>'已禁用', - 'disable'=>'禁用', - 'Enabled'=>'已启用', - 'enable'=>'启用', - 'Install' => '安装', - 'Uninstall' => '卸载', - 'Select' => '选择', - 'Address' => '地址', - 'Login Tips' => '登录提示:此处为FunAdmin官方账号', - 'Your Password' => '您的密码', - 'UserName Or Email' => '账号或密码', - 'Please backup your data before upgrading!!!'=>'升级前请备份数据!!!', - "Are you sure you want to install it"=>"确定安装吗?", - "Are you sure you want to uninstall it"=>"确定卸载吗?", - 'Are you sure you want to change it'=>'确定修改吗?' + 'addon name is not right' => '插件名字不正确', + 'addon can not be empty' => '插件不能为空', + 'addon %s can not be empty' => '插件 %s 不能为空', + 'addon config is not found' => '找不到插件配置', + 'addons %s is already installed' => '插件%s已安装', + 'addons %s is not ready' => '插件%s还没有准备好', + 'Please disable addons %s first' => '请先禁用插件%s', + 'addon uninstall fail' => '插件卸载失败', + 'Uninstall successful' => '插件卸载成功', + 'LOGO' => 'Logo', + 'Create' => '创建', + 'title' => '标题', + 'Account' => '云账号', + 'Addon version' => '版本', + 'Addon Config' => '插件配置', + 'Addon require' => '需求版本', + 'author' => '作者', + 'publishtime' => '发布时间', + 'localinstall' => '离线安装', + 'local install' => '离线安装', + 'price' => '价格', + 'download' => '下载量', + 'Logo' => '图标', + 'ADDONAME' => '插件名', + 'config' => '配置', + 'install' => '安装', + 'Ver' => '版本', + 'Force' => '强制', + 'Requires' => '需求版本', + 'uninstall' => '卸载', + 'disabled' => '已禁用', + 'disable' => '禁用', + 'Enabled' => '已启用', + 'enable' => '启用', + 'Install' => '安装', + 'Uninstall' => '卸载', + 'Select' => '选择', + 'Address' => '地址', + 'Login Tips' => '登录提示:此处为FunAdmin官方账号', + 'Your Password' => '您的密码', + 'UserName Or Email' => '账号或密码', + 'Please backup your data before upgrading!!!' => '升级前请备份数据!!!', + "Are you sure you want to install it" => "确定安装吗?", + "Are you sure you want to uninstall it" => "确定卸载吗?", + 'Are you sure you want to change it' => '确定修改吗?' ]; diff --git a/app/backend/lang/zh-cn/auth/admin.php b/app/backend/lang/zh-cn/auth/admin.php index 8ff357fc6..16159f435 100644 --- a/app/backend/lang/zh-cn/auth/admin.php +++ b/app/backend/lang/zh-cn/auth/admin.php @@ -1,17 +1,17 @@ '角色组', - 'oldpassword' => '旧密码', - 'NewPasswd' => '新密码', - 'Renewpasswd' => '重复新密码', - 'Group_Id' => '角色组', - 'Supper man cannot delete' => '超级管理员不能删除', - 'Supper man cannot edit' => '超级管理员不能修改', - 'Supper man cannot edit state' => '超级管理员不能修改状态', - 'Origin Password Error' => '原密码错误', - 'Username is between 4 and 25 characters'=>'用户名在4-25个字符', - 'Password must be greater than 6 characters and less than 15 characters'=>'密码在6-15个字符', - 'realname' => '姓名', - 'For password retrieval please fill in carefully'=>'找回密码请认真填写', -]; + return [ + 'AuthGroup' => '角色组', + 'oldpassword' => '旧密码', + 'NewPasswd' => '新密码', + 'Renewpasswd' => '重复新密码', + 'Group_Id' => '角色组', + 'Supper man cannot delete' => '超级管理员不能删除', + 'Supper man cannot edit' => '超级管理员不能修改', + 'Supper man cannot edit state' => '超级管理员不能修改状态', + 'Origin Password Error' => '原密码错误', + 'Username is between 4 and 25 characters' => '用户名在4-25个字符', + 'Password must be greater than 6 characters and less than 15 characters' => '密码在6-15个字符', + 'realname' => '姓名', + 'For password retrieval please fill in carefully'=> '找回密码请认真填写', + ]; \ No newline at end of file diff --git a/app/backend/lang/zh-cn/auth/auth.php b/app/backend/lang/zh-cn/auth/auth.php index c2e0ab9c7..80aebc4c9 100644 --- a/app/backend/lang/zh-cn/auth/auth.php +++ b/app/backend/lang/zh-cn/auth/auth.php @@ -1,28 +1,37 @@ '图标', - 'Auth Name' =>'权限名', - 'AuthName' =>'权限名', - 'Auth Verify' =>'权限验证', - 'Menu Status' =>'菜单状态', - 'Controller/Action' =>'控制器/方法', - 'Module/Controller/Action' =>'模块/控制器/方法', - 'Add Child' =>'添加下级', - 'Menu Target'=>'窗口目标', - 'MenuStatus'=>'菜单状态', - 'Menu_Status'=>'菜单状态', - 'Target'=>'窗口', - 'href'=>'路由', - '_self'=>'当前', - '_blank'=>'空窗口', - '_parent'=>'上级', - '_top'=>'顶级', - 'IsMenu'=>'是否菜单', - 'title'=>'菜单名', - 'Module'=>'模块', - 'Parent menu cannot be modified to submenu'=>'父菜单不能修改到子菜单', - 'The superior cannot be set as himself'=>'父级不能为自己', - 'setValue'=>'设置值', - 'upme' =>'更新信息', - 'Are you sure you want to delete menu and children menu!!!'=>'确定要删除菜单和子菜单吗!!!' -]; \ No newline at end of file + 'icon' => '图标', + 'Auth Name' => '权限名', + 'AuthName' => '权限名', + 'Auth Verify' => '权限验证', + 'Menu Status' => '菜单状态', + 'Controller/Action' => '控制器/方法', + 'Module/Controller/Action' => '模块/控制器/方法', + 'Add Child' => '添加下级', + 'Menu Target' => '窗口目标', + 'MenuStatus' => '菜单状态', + 'Menu_Status' => '菜单状态', + 'Target' => '窗口', + 'href' => '路由', + '_self' => '当前', + '_blank' => '空窗口', + '_parent' => '上级', + '_top' => '顶级', + 'IsMenu' => '是否菜单', + 'title' => '菜单名', + 'Module' => '模块', + 'Parent menu cannot be modified to submenu' => '父菜单不能修改到子菜单', + 'The superior cannot be set as himself' => '父级不能为自己', + 'setValue' => '设置值', + 'upme' => '更新信息', + 'Are you sure you want to delete menu and children menu!!!' => '确定要删除菜单和子菜单吗!!!' +]; diff --git a/app/backend/lang/zh-cn/auth/authgroup.php b/app/backend/lang/zh-cn/auth/authgroup.php index 1b67b385a..7404d0922 100644 --- a/app/backend/lang/zh-cn/auth/authgroup.php +++ b/app/backend/lang/zh-cn/auth/authgroup.php @@ -1,10 +1,9 @@ '分配角色', - 'GroupName'=>'组名', - 'AuthGroupPid'=>'角色组ID', - 'Module'=>'模块', - "SupperAdmin cannot edit"=>'超管组不能被编辑', - + 'access group' => '分配角色', + 'GroupName' => '组名', + 'AuthGroupPid' => '角色组ID', + 'Module' => '模块', + "SupperAdmin cannot edit" => '超管组不能被编辑', ]; diff --git a/app/backend/lang/zh-cn/login.php b/app/backend/lang/zh-cn/login.php index 99f90fafe..3ec2b74e5 100644 --- a/app/backend/lang/zh-cn/login.php +++ b/app/backend/lang/zh-cn/login.php @@ -3,11 +3,11 @@ return [ // 登录 - 'Keep Login'=>"保持登录", - 'Captcha error' => '验证码错误', - 'Login Success' => '登录成功', - 'Login Failed' => '登录失败', - 'Account is disabled' => '账号禁用', - 'Account is not accessed' => '账号未成功接入', + 'Keep Login' => "保持登录", + 'Captcha error' => '验证码错误', + 'Login Success' => '登录成功', + 'Login Failed' => '登录失败', + 'Account is disabled' => '账号禁用', + 'Account is not accessed' => '账号未成功接入', 'Please check username or password' => '请检查用户名或密码', ]; \ No newline at end of file diff --git a/app/backend/lang/zh-cn/member/member.php b/app/backend/lang/zh-cn/member/member.php index f7c3cb3a6..a8e76879a 100644 --- a/app/backend/lang/zh-cn/member/member.php +++ b/app/backend/lang/zh-cn/member/member.php @@ -1,40 +1,38 @@ "会员", - 'GroupName' =>"会员组", - 'Memberadd' =>"会员添加", - 'Memberdel' =>"会员删除", - 'Memberedit' =>"会员编辑", - 'MemberLevel' =>"会员等级", - 'Level' =>"等级", - 'Membername' =>"名称 ", - 'Nickname' => '昵称', - 'Password' => '密码', - 'Salt' => '密码盐', - 'Email' => '电子邮箱', - 'Mobile' => '手机号', - 'Avatar' => '头像', - 'Sex' => '性别', - 'Male' => '男', - 'FeMale' => '女', - 'Secret' => '保密', - 'Birthday' => '生日', - 'Score' => '积分', - 'Registertime' =>'注册时间', - 'Lastlogintime' => '上次登录时间', - 'Logintime' => '登录时间', - 'Loginip' => '登录IP', - 'Loginfailure' => '失败次数', - 'Registerip' => '加入IP', - 'Token' => 'Token', - 'Status' => '状态', - 'level_id' => '会员id', - 'group_id' => '会员组id', - 'Man' => '男性', - 'Female' => '女性', - 'Secrecy' => '保密', - 'Between 4 and 25 characters' =>'4-25个字符', - '100 not discounted 90% off' =>'100 不打折,90% 折扣' - - - ]; \ No newline at end of file + 'Member' => "会员", + 'GroupName' => "会员组", + 'Memberadd' => "会员添加", + 'Memberdel' => "会员删除", + 'Memberedit' => "会员编辑", + 'MemberLevel' => "会员等级", + 'Level' => "等级", + 'Membername' => "名称 ", + 'Nickname' => '昵称', + 'Password' => '密码', + 'Salt' => '密码盐', + 'Email' => '电子邮箱', + 'Mobile' => '手机号', + 'Avatar' => '头像', + 'Sex' => '性别', + 'Male' => '男', + 'FeMale' => '女', + 'Secret' => '保密', + 'Birthday' => '生日', + 'Score' => '积分', + 'Registertime' => '注册时间', + 'Lastlogintime' => '上次登录时间', + 'Logintime' => '登录时间', + 'Loginip' => '登录IP', + 'Loginfailure' => '失败次数', + 'Registerip' => '加入IP', + 'Token' => 'Token', + 'Status' => '状态', + 'level_id' => '会员id', + 'group_id' => '会员组id', + 'Man' => '男性', + 'Female' => '女性', + 'Secrecy' => '保密', + 'Between 4 and 25 characters' => '4-25个字符', + '100 not discounted 90% off' => '100 不打折,90% 折扣' +]; \ No newline at end of file diff --git a/app/backend/lang/zh-cn/member/membergroup.php b/app/backend/lang/zh-cn/member/membergroup.php index 5210f56be..ca9c2f83f 100644 --- a/app/backend/lang/zh-cn/member/membergroup.php +++ b/app/backend/lang/zh-cn/member/membergroup.php @@ -1,13 +1,11 @@ "会员 ", - 'GroupName' =>"分组名", - 'GroupAdd' =>"分组名添加", - 'GroupEdit' =>"分组名编辑", - 'GroupDel' =>"分组名删除", - 'GroupModify' =>"分组名修改", - 'Status' => '状态', - 'Rules' => '规则', - - + 'Member' => "会员", + 'GroupName' => "分组名", + 'GroupAdd' => "分组名添加", + 'GroupEdit' => "分组名编辑", + 'GroupDel' => "分组名删除", + 'GroupModify' => "分组名修改", + 'Status' => '状态', + 'Rules' => '规则', ]; \ No newline at end of file diff --git a/app/backend/lang/zh-cn/member/memberlevel.php b/app/backend/lang/zh-cn/member/memberlevel.php index 29dc12817..783f495f1 100644 --- a/app/backend/lang/zh-cn/member/memberlevel.php +++ b/app/backend/lang/zh-cn/member/memberlevel.php @@ -1,16 +1,16 @@ "等级", - 'Leveladd' =>"等级添加", - 'Leveldel' =>"等级删除", - 'Leveledit' =>"等级编辑", - 'Leveldesc' =>"等级描述", - 'LevelName' =>"等级名字", - 'Levelmoney' =>"等级金额", - 'Leveldiscount' =>"等级折扣 ", - 'Amount' =>"金额 ", - 'Discount' =>"折扣", - 'Thumb' =>"缩略图", - 'Between 4 and 25 characters' =>'4-25个字符', - '100 not discounted 90% off' =>'100 不打折,90% 折扣' + 'Level' => "等级", + 'Leveladd' => "等级添加", + 'Leveldel' => "等级删除", + 'Leveledit' => "等级编辑", + 'Leveldesc' => "等级描述", + 'LevelName' => "等级名字", + 'Levelmoney' => "等级金额", + 'Leveldiscount' => "等级折扣", + 'Amount' => "金额", + 'Discount' => "折扣", + 'Thumb' => "缩略图", + 'Between 4 and 25 characters' => "4-25个字符", + '100 not discounted 90% off' => "100 不打折,90% 折扣" ]; \ No newline at end of file diff --git a/app/backend/lang/zh-cn/sys/adminlog.php b/app/backend/lang/zh-cn/sys/adminlog.php index 1122eb34e..82fc32b28 100644 --- a/app/backend/lang/zh-cn/sys/adminlog.php +++ b/app/backend/lang/zh-cn/sys/adminlog.php @@ -1,11 +1,11 @@ '管理员名', - 'Method'=>'方式', - 'Log Addr'=>'日志地址', - 'Log Content'=>'日志内容', - 'Log Title'=>'日志标题', - 'Log Agent'=>'代理', - 'Ip'=>'ip地址', + 'Admin Username' => '管理员名', + 'Method' => '方式', + 'Log Addr' => '日志地址', + 'Log Content' => '日志内容', + 'Log Title' => '日志标题', + 'Log Agent' => '代理', + 'Ip' => 'ip地址', ]; \ No newline at end of file diff --git a/app/backend/lang/zh-cn/sys/attach.php b/app/backend/lang/zh-cn/sys/attach.php index 27e082b94..43788e903 100644 --- a/app/backend/lang/zh-cn/sys/attach.php +++ b/app/backend/lang/zh-cn/sys/attach.php @@ -1,27 +1,26 @@ '附件', - 'zip'=>'压缩件', - 'text'=>'文本', - 'file'=>'文件', - 'image'=>'图片', - 'video'=>'视频', - 'audio'=>'音频', - 'OriginalName'=>'原名', - 'name'=>'名字', - 'thumb'=>'缩略图', - 'FileType'=>'文件类型', - 'Ext'=>'后缀', - 'url'=>'路径', - 'Path'=>'路径', - 'Size'=>'大小', - 'Driver'=>'驱动', - 'Mime'=>'mime类型', - 'Userid'=>'用户id', - 'Width'=>'宽', - 'Height'=>'高', - 'Group List'=>'分组', - 'ALL OR DEFAULT CAN NOT DELETE'=>"默认或全部不能删除", - + 'Attach' => '附件', + 'zip' => '压缩件', + 'text' => '文本', + 'file' => '文件', + 'image' => '图片', + 'video' => '视频', + 'audio' => '音频', + 'OriginalName' => '原名', + 'name' => '名字', + 'thumb' => '缩略图', + 'FileType' => '文件类型', + 'Ext' => '后缀', + 'url' => '路径', + 'Path' => '路径', + 'Size' => '大小', + 'Driver' => '驱动', + 'Mime' => 'mime类型', + 'Userid' => '用户id', + 'Width' => '宽', + 'Height' => '高', + 'Group List' => '分组', + 'ALL OR DEFAULT CAN NOT DELETE' => '默认或全部不能删除', ]; diff --git a/app/backend/lang/zh-cn/sys/attachgroup.php b/app/backend/lang/zh-cn/sys/attachgroup.php index 1f1692c6d..06544be4c 100644 --- a/app/backend/lang/zh-cn/sys/attachgroup.php +++ b/app/backend/lang/zh-cn/sys/attachgroup.php @@ -1,28 +1,28 @@ '组名', - 'Attach'=>'附件', - 'zip'=>'压缩件', - 'text'=>'文本', - 'file'=>'文件', - 'Thumb'=>'图片', - 'video'=>'视频', - 'audio'=>'音频', - 'OriginalName'=>'原名', - 'name'=>'名字', - 'thumb'=>'缩略图', - 'FileType'=>'文件类型', - 'Ext'=>'后缀', - 'url'=>'路径', - 'Path'=>'路径', - 'Size'=>'大小', - 'Driver'=>'驱动', - 'Mime'=>'mime类型', - 'Userid'=>'用户id', - 'Width'=>'宽', - 'Height'=>'高', - 'Group List'=>'分组', - 'ALL OR DEFAULT CAN NOT DELETE'=>"默认或全部不能删除", + 'title' => '组名', + 'Attach' => '附件', + 'zip' => '压缩件', + 'text' => '文本', + 'file' => '文件', + 'Thumb' => '图片', + 'video' => '视频', + 'audio' => '音频', + 'OriginalName' => '原名', + 'name' => '名字', + 'thumb' => '缩略图', + 'FileType' => '文件类型', + 'Ext' => '后缀', + 'url' => '路径', + 'Path' => '路径', + 'Size' => '大小', + 'Driver' => '驱动', + 'Mime' => 'mime类型', + 'Userid' => '用户id', + 'Width' => '宽', + 'Height' => '高', + 'Group List' => '分组', + 'ALL OR DEFAULT CAN NOT DELETE'=> '默认或全部不能删除', ]; diff --git a/app/backend/lang/zh-cn/sys/config.php b/app/backend/lang/zh-cn/sys/config.php index b3036972b..fa97a7541 100644 --- a/app/backend/lang/zh-cn/sys/config.php +++ b/app/backend/lang/zh-cn/sys/config.php @@ -3,71 +3,71 @@ return [ // 配置组 - 'Field already exist' => '字段已经存在', - 'Config Code'=>'配置键', - 'Config Value'=>'配置值', - 'Config Default Value'=>'配置默认值', - 'Config Extra Value'=>'配置附加值', - 'Config Group' => '配置分组', - 'Config Remark' => '配置备注', - 'Group name' => '分组名', - 'Group title' => '分组标题', - 'Group has config' => '分组下有配置', - 'Site Setting' => '网站设置', - 'Email Setting' => '邮箱设置', - 'Email Address' => '邮箱地址', - 'Sms setting' => '短信设置', - 'Upload setting' => '上传设置', - 'Remark' =>"备注", - 'Default_value' =>"默认值", - 'Extra' =>"附加", - 'Verify' =>"验证", - 'value' =>"值", - 'Default Value'=>'默认值', - 'Type'=>'类型', - 'Identifier' => '识别码', - 'Code' => '识别码', - 'Remarks'=>'备注', - 'English characters Canot duplicate the current existing'=>'英文字符,不能重复', - 'default'=>'默认', - 'Sms appid' => '短信APPID', - 'Sms appsecret' => '短密钥', - 'Sms product name' => '短信产品名', - 'Sms template id' => '短信模板id', + 'Field already exist' => '字段已经存在', + 'Config Code' => '配置键', + 'Config Value' => '配置值', + 'Config Default Value' => '配置默认值', + 'Config Extra Value' => '配置附加值', + 'Config Group' => '配置分组', + 'Config Remark' => '配置备注', + 'Group name' => '分组名', + 'Group title' => '分组标题', + 'Group has config' => '分组下有配置', + 'Site Setting' => '网站设置', + 'Email Setting' => '邮箱设置', + 'Email Address' => '邮箱地址', + 'Sms setting' => '短信设置', + 'Upload setting' => '上传设置', + 'Remark' => '备注', + 'Default_value' => '默认值', + 'Extra' => '附加', + 'Verify' => '验证', + 'value' => '值', + 'Default Value' => '默认值', + 'Type' => '类型', + 'Identifier' => '识别码', + 'Code' => '识别码', + 'Remarks' => '备注', + 'English characters Canot duplicate the current existing' => '英文字符,不能重复', + 'default' => '默认', + 'Sms appid' => '短信APPID', + 'Sms appsecret' => '短密钥', + 'Sms product name' => '短信产品名', + 'Sms template id' => '短信模板id', - 'Upload Driver'=>"上传驱动", - 'local'=>'本地', - 'Alioss'=>'阿里oss', - 'Qiniuoss'=>'七牛oss', - 'Tencos'=>'腾讯oss', - 'Email server' => '邮箱服务器', - 'Email address' => '邮箱地址', - 'Email port' => '邮箱端口', - 'Server mailbox protocol' => '邮箱协议', - 'Email password' => '邮箱授权密码', - 'Send email person' => '发送者邮箱', + 'Upload Driver' => '上传驱动', + 'local' => '本地', + 'Alioss' => '阿里oss', + 'Qiniuoss' => '七牛oss', + 'Tencos' => '腾讯oss', + 'Email server' => '邮箱服务器', + 'Email address' => '邮箱地址', + 'Email port' => '邮箱端口', + 'Server mailbox protocol' => '邮箱协议', + 'Email password' => '邮箱授权密码', + 'Send email person' => '发送者邮箱', - 'All right' => '所有权', - 'Site name' => '网站名', - 'Site title' => '网站标题', - 'Site_logo' => '网站logo', - 'Site email' => '网站邮箱', - 'Site domain' => '网站域名', - 'Meta keywords' => '网站关键字', - 'Meta description' => '网站描述', - 'Qrcode' => "二维码", - 'Tips' => "提示", - 'Maxfilesize' => "最大文件大小", - 'Size' => "文件大小", - 'File type' => "文件类型", - 'Wateralpha'=>'水印深度', - 'Water'=>'开启水印', - 'WaterPosition'=>'水印位置', - 'Watersize'=>'水印大小', - 'watercolor'=>'水印颜色', - 'Image/Text'=>'图片或文字', - 'water setting'=>'水印设置', - 'set'=>'配置', - 'Editor'=>"编辑器" + 'All right' => '所有权', + 'Site name' => '网站名', + 'Site title' => '网站标题', + 'Site_logo' => '网站logo', + 'Site email' => '网站邮箱', + 'Site domain' => '网站域名', + 'Meta keywords' => '网站关键字', + 'Meta description' => '网站描述', + 'Qrcode' => '二维码', + 'Tips' => '提示', + 'Maxfilesize' => '最大文件大小', + 'Size' => '文件大小', + 'File type' => '文件类型', + 'Wateralpha' => '水印深度', + 'Water' => '开启水印', + 'WaterPosition' => '水印位置', + 'Watersize' => '水印大小', + 'watercolor' => '水印颜色', + 'Image/Text' => '图片或文字', + 'water setting' => '水印设置', + 'set' => '配置', + 'Editor' => '编辑器' ]; diff --git a/app/backend/lang/zh-cn/sys/configgroup.php b/app/backend/lang/zh-cn/sys/configgroup.php index 0e867564d..232f24ad6 100644 --- a/app/backend/lang/zh-cn/sys/configgroup.php +++ b/app/backend/lang/zh-cn/sys/configgroup.php @@ -1,9 +1,9 @@ '配置分组', - 'Group name' => '分组名', - 'Group title' => '分组标题', - 'Group has config' => '分组下有配置', + 'Config Group' => '配置分组', + 'Group name' => '分组名', + 'Group title' => '分组标题', + 'Group has config' => '分组下有配置', ]; \ No newline at end of file diff --git a/app/backend/middleware/CheckRole.php b/app/backend/middleware/CheckRole.php index ad742e21c..4d4a4859a 100644 --- a/app/backend/middleware/CheckRole.php +++ b/app/backend/middleware/CheckRole.php @@ -20,11 +20,14 @@ class CheckRole public function handle($request, \Closure $next) { if(!Session::has('admin')) { - - $this->redirect(__u('backend/login/index')); + if($request->baseFile()!='/index.php' && str_contains($request->baseFile(), '.php')) { + $this->error('Please login first',url('login/index')); + }else{ + $this->error('Please login first'); + } } $auth = AuthService::instance(); - $auth->checkNode(); + $auth->roleAccess(); //中间件handle方法的返回值必须是一个Response对象。 return $next($request); } diff --git a/app/backend/middleware/ViewNode.php b/app/backend/middleware/ViewNode.php index 88a9cc5da..ffa2c29ed 100644 --- a/app/backend/middleware/ViewNode.php +++ b/app/backend/middleware/ViewNode.php @@ -4,7 +4,6 @@ use app\backend\service\AuthService; use think\App; -use think\facade\Config; use think\facade\Lang; use think\facade\Request; use think\facade\View; @@ -13,14 +12,13 @@ class ViewNode { public function handle($request, \Closure $next) { - [$appname, $controllername, $actionname] = [app('http')->getName(), $request->controller(), Request::action()]; + [$appname, $controllername, $actionname] = [app('http')->getName(), $request->controller(true), Request::action()]; $controllers = explode('.', $controllername); $jsname = ''; foreach ($controllers as $vo) { - empty($jsname) ? $jsname = strtolower(Str::camel(parse_name($vo))) : $jsname .= '/' . strtolower(Str::camel(parse_name($vo))); + empty($jsname) ? $jsname = $vo : $jsname .= '/' . $vo; } - $controllername = strtolower(Str::camel(parse_name($controllername))); - $actionname = strtolower(Str::camel(parse_name($actionname))); + $actionname = strtolower($actionname); $requesturl = "{$appname}/{$controllername}/{$actionname}"; $autojs = file_exists(app()->getRootPath()."public".DS."static".DS."{$appname}".DS."js".DS."{$jsname}.js") ? true : false; $jspath ="{$appname}/js/{$jsname}.js"; @@ -37,9 +35,12 @@ public function handle($request, \Closure $next) 'lang' => strip_tags(Lang::getLangset()), 'site' => syscfg('site'), 'upload' => syscfg('upload'), - 'publicAjaxUrl' =>config('funadmin.publicAjaxUrl'), + 'editor' => syscfg('editor'), + 'public_ajax_url' =>config('funadmin.public_ajax_url'), + 'api_domain' =>config('funadmin.api_domain'), + 'api_login_url' =>config('funadmin.api_login_url'), ]; - View::assign('config',$config); + View::assign('CONFIG',$config); $request->appname =$appname; return $next($request); } diff --git a/app/backend/model/Admin.php b/app/backend/model/Admin.php index 0fcba8101..c7fa42cfe 100644 --- a/app/backend/model/Admin.php +++ b/app/backend/model/Admin.php @@ -24,8 +24,7 @@ class Admin extends BackendModel { - protected $deleteTime = 'delete_time'; - protected $defaultSoftDelete = 0; + public function __construct(array $data = []) diff --git a/app/backend/model/AdminLog.php b/app/backend/model/AdminLog.php index 5c2345558..1d65a1e70 100644 --- a/app/backend/model/AdminLog.php +++ b/app/backend/model/AdminLog.php @@ -23,8 +23,7 @@ class AdminLog extends BackendModel - protected $deleteTime = 'delete_time'; - protected $defaultSoftDelete = 0; + public function __construct(array $data = []) { diff --git a/app/backend/model/AttachGroup.php b/app/backend/model/AttachGroup.php index a4a620d6a..a22c8673e 100644 --- a/app/backend/model/AttachGroup.php +++ b/app/backend/model/AttachGroup.php @@ -26,9 +26,6 @@ class AttachGroup extends BackendModel use SoftDelete; - - protected $deleteTime = 'delete_time'; - protected $defaultSoftDelete = 0; public function __construct(array $data = []) { diff --git a/app/backend/model/AuthGroup.php b/app/backend/model/AuthGroup.php index e7afce6a8..aaaed940a 100644 --- a/app/backend/model/AuthGroup.php +++ b/app/backend/model/AuthGroup.php @@ -27,8 +27,7 @@ class AuthGroup extends BackendModel - protected $deleteTime = 'delete_time'; - protected $defaultSoftDelete = 0; + public function __construct(array $data = []) { diff --git a/app/backend/model/AuthRule.php b/app/backend/model/AuthRule.php index 4ed5899b7..6c93699a7 100644 --- a/app/backend/model/AuthRule.php +++ b/app/backend/model/AuthRule.php @@ -24,8 +24,7 @@ class AuthRule extends BackendModel use SoftDelete; - protected $deleteTime = 'delete_time'; - protected $defaultSoftDelete = 0; + public function __construct(array $data = []) { diff --git a/app/backend/model/Member.php b/app/backend/model/Member.php index 284d25129..67cc90664 100644 --- a/app/backend/model/Member.php +++ b/app/backend/model/Member.php @@ -26,17 +26,16 @@ class Member extends BackendModel { - protected $deleteTime = 'delete_time'; - protected $defaultSoftDelete = 0; + public function __construct(array $data = []) { parent::__construct($data); } - public function memberGroup(){ + public function group(){ return $this->belongsTo('MemberGroup','group_id','id'); } - public function memberLevel() + public function level() { return $this->belongsTo('MemberLevel', 'level_id', 'id', [], 'LEFT'); } diff --git a/app/backend/model/MemberGroup.php b/app/backend/model/MemberGroup.php index abc5a077d..a8857ca40 100644 --- a/app/backend/model/MemberGroup.php +++ b/app/backend/model/MemberGroup.php @@ -25,8 +25,7 @@ class MemberGroup extends BackendModel { - protected $deleteTime = 'delete_time'; - protected $defaultSoftDelete = 0; + public function __construct(array $data = []) { diff --git a/app/backend/model/MemberLevel.php b/app/backend/model/MemberLevel.php index 6abbcfbd4..13671aac6 100644 --- a/app/backend/model/MemberLevel.php +++ b/app/backend/model/MemberLevel.php @@ -25,8 +25,7 @@ class MemberLevel extends BackendModel - protected $deleteTime = 'delete_time'; - protected $defaultSoftDelete = 0; + public function __construct(array $data = []) { diff --git a/app/backend/service/AddonService.php b/app/backend/service/AddonService.php index 7b0602e61..581534732 100644 --- a/app/backend/service/AddonService.php +++ b/app/backend/service/AddonService.php @@ -15,6 +15,7 @@ namespace app\backend\service; use app\backend\model\AuthRule; +use app\common\model\Addon; use app\common\service\AbstractService; use app\common\traits\Jump; use fun\helper\SignHelper; @@ -22,11 +23,11 @@ use think\db\exception\PDOException; use think\Exception; use think\facade\Cache; -use think\facade\Config; use think\facade\Cookie; use think\facade\Db; use think\facade\Request; use think\facade\Session; +use fun\addons\Service; class AddonService extends AbstractService { @@ -35,6 +36,7 @@ class AddonService extends AbstractService protected $myaddon = 'myaddon'; public function __construct() { + parent::__construct(); } //添加菜单 @@ -123,8 +125,199 @@ public function delMenuCache(){ Cache::delete('adminMenus_' . session('admin.id')); } - - + /** + * 安装插件 + * @param string $name + * @param string $type + * @return bool + */ + public function installAddon(string $name,string $type=''){ + $class = get_addons_instance($name); + if (empty($class)) { + throw new Exception(lang('addons %s is not ready', [$name])); + } + $addon_info = get_addons_info($name); + if(!empty($addon_info['depend'])){ + $depend = explode(',',$addon_info['depend']); + foreach ($depend as $v) { + $dependAddon = get_addons_info($v); + if(empty($dependAddon)){ + throw new Exception('Please install the dependent plugin first: '.$addon_info['depend']); + } + } + } + //添加数据库 + try{ + if($type!='upgrade'){ + importsql($name); + }else{ + $sqlFile = root_path().'addons/'.$name.'/'.'upgrade.sql'; + if(!file_exists($sqlFile)){ + $sqlFile = root_path().'addons/'.$name.'/'.'update.sql'; + } + if(file_exists($sqlFile)){ + importsql($name,$sqlFile); + } + } + } catch (Exception $e){ + throw new Exception($e->getMessage()); + } + // 安装菜单 + $menu_config = get_addons_menu($name); + if(!empty($menu_config)){ + list($menu,$pid) = $this->getMenu($menu_config); + $this->addAddonMenu($menu,$pid,$name); + } + $model = new Addon(); + //安装插件 + $class->install(); + $addon_info['status'] = 1; + $list = $this->isInstall($name); + if($list){ + if($list->delete_time > 0){ + $model->restore(['id'=>$list->id]); + } + $res = $model->update(['status'=>1],['id'=>$list->id]); + }else{ + $res = $model->save($addon_info); + } + if (!$res) { + throw new Exception(lang('addon install fail')); + } + Service::copyApp($name,$delete = true); + try { + Service::updateAddonsInfo($name); + //刷新addon文件 + refreshaddons(); + }catch (Exception $e){ + throw new Exception($e->getMessage()); + } + return true; + } + public function uninstallAddon(string $name){ + try { + if (!$name) { + throw new Exception(lang(' addon name can not be empty')); + } + //插件名匹配 + if (!preg_match("/^[a-zA-Z0-9]+$/", $name)) { + throw new Exception(lang('addon name is not right')); + } + $model = new Addon(); + //获取插件信息 + $info = $model->withTrashed()->where('name', $name)->find(); + if (empty($info)) { + throw new Exception(lang('addon is not exist')); + } + if($info->status==1){ + throw new Exception(lang('Please disable addons %s first',[$name])); + } + if (!$info->force()->delete()) { + throw new Exception(lang('addon uninstall fail')); + } + //卸载插件 + $class = get_addons_instance($name); + $class->uninstall(); + //删除菜单 + $menu_config=get_addons_menu($name); + if(!empty($menu_config)){ + list($menu,$pid) = $this->getMenu($menu_config); + $this->delAddonMenu($menu,$name); + } + //卸载sql; + uninstallsql($name); + //还原文件 + Service::removeApp($name,$delete= true); + Service::updateAddonsInfo($name,1,0); + refreshaddons(); + } catch (Exception $e) { + throw new Exception($e->getMessage()); + } + return true; + } + /** + * @NodeAnnotation (title="是否安装") + * @param $name + * @return array|false|\think\Model|null + * @throws \think\dbException\DataNotFoundException + * @throws \think\dbException\DbException + * @throws \think\dbException\ModelNotFoundException + */ + public function isInstall($name) + { + if (empty($name)) { + return false; + } + $addons = Addon::withTrashed()->where('name', $name)->find(); + return $addons; + } + /** + * 获取菜单 + * @param mixed $config + * @return array + */ + public function getMenu($config = []) + { + $is_nav = $config['is_nav']??1; + $menuArr = $config['menu']; + $menu = []; + if(!empty($menuArr[0]) && is_array($menuArr[0])){ + foreach ($menuArr as $value) { + if($is_nav==-1){ + $menu = array_merge($menu,$value['menulist']); + $pid = 0; + }elseif($is_nav==0){ + $menu[] = $value; + $pid = $this->addAddonManager()->id; + }else{ + $menu[] = $value; + $pid = 0; + } + } + }else{ + if($is_nav==-1){ + $menu = array_merge($menu,$menuArr['menulist']); + $pid = 0; + }elseif($is_nav==0){ + $menu[] = $menuArr; + $pid = $this->addAddonManager()->id; + }else{ + $menu[] = $menuArr; + $pid = 0; + } + } + return [$menu,$pid]; + } + /** + * 修改插件状态 + * @param string $name + * @return void + */ + public function modifyAddon(string $name){ + $info = Addon::where('name',$name)->find(); + $addoninfo = get_addons_info($name); + $addoninfo['status'] = $addoninfo['status']?0:1; + try { + $info->status =$addoninfo['status']; + Service::updateAddonsInfo($name,$addoninfo['status']); + // 安装菜单 + $class = get_addons_instance($name); + $menu_config = get_addons_menu($name); + if(!empty($menu_config)){ + list($menu,$pid) = $this->getMenu($menu_config); + if( $addoninfo['status']){ + $this->addAddonMenu($menu,$pid,$name); + }else{ + $this->delAddonMenu($menu,$name); + } + } + refreshaddons(); + $info->save(); + $addoninfo['status']==1 ?$class->enabled():$class->disabled(); + }catch (Exception $e){ + throw new Exception($e->getMessage()); + } + } } diff --git a/app/backend/service/AuthService.php b/app/backend/service/AuthService.php index 3320961b5..47935be75 100644 --- a/app/backend/service/AuthService.php +++ b/app/backend/service/AuthService.php @@ -22,7 +22,6 @@ use app\common\traits\Jump; use fun\helper\SignHelper; use think\facade\Cache; -use think\facade\Config; use think\facade\Cookie; use think\facade\Request; use think\facade\Session; @@ -58,6 +57,11 @@ class AuthService extends AbstractService * @var $hrefId ; */ protected $hrefId; + /** + * 当前管理员权限规则id + * @var array + */ + protected $adminRules = []; /** * 获取用户信息 @@ -71,7 +75,8 @@ public function __get($name) public function __construct() { - if ($auth = Config::get('auth')) { + parent::__construct(); + if ($auth = config('auth')) { $this->config = array_merge($this->config, $auth); } // 初始化request @@ -92,29 +97,40 @@ public function __construct() /** * 权限节点 + * @return array|int[]|mixed|string[] */ public function nodeList() { $allAuthNode = []; if (session('admin')) { - $cacheKey = 'allAuthNode_' . session('admin.id'); + $cacheKey = 'auth-node-list-' . session('admin.id'); $allAuthNode = Cache::get($cacheKey); if (empty($allAuthNode)) { $allAuthIds = $this->getRules(session('admin.group_id')); - $allAuthNode = AuthRule::where('status', 1)->whereIn('id', $allAuthIds)->cache($cacheKey)->column('href', 'href'); - foreach ($allAuthNode as $k => $v) { - $allAuthNode[$k] = (parse_name($v, 1)); - } - $allAuthNode = array_flip($allAuthNode); + $allAuthNode = db_cache($cacheKey,function()use($allAuthIds,$cacheKey){ + $authNode = AuthRule::where('status', 1)->whereIn('id', $allAuthIds)->cache($cacheKey)->column('href', 'href'); + foreach ($authNode as $k => $v) { + $authNode[$k] = (parse_name($v, 1)); + } + return array_flip( $authNode); + }); + Cache::set($cacheKey, $allAuthNode,3600); + } } return $allAuthNode; } - /* - * 菜单排列 - */ + /** + * 菜单节点 + * @param $cate + * @param $lefthtml + * @param $pid + * @param $lvl + * @param $leftpin + * @return array + */ public function treemenu($cate, $lefthtml = '├─', $pid = 0, $lvl = 0, $leftpin = 0) { $arr = array(); @@ -132,8 +148,12 @@ public function treemenu($cate, $lefthtml = '├─', $pid = 0, $lvl = 0, $leftp return $arr; } - /* - * 权限 + /** + * Summary of auth + * @param mixed $cate + * @param mixed $rules + * @param mixed $pid + * @return array */ public function auth($cate, $rules, $pid = 0) { @@ -154,9 +174,10 @@ public function auth($cate, $rules, $pid = 0) /** * 权限设置选中状态 - * @param $cate 栏目 - * @param int $pid 父ID - * @param $rules 规则 + * @param array $cate + * @param int $pid + * @param string $rules + * @param int $group_id * @return array */ public function authChecked(array $cate, int $pid, string $rules, int $group_id) @@ -183,9 +204,7 @@ public function authChecked(array $cate, int $pid, string $rules, int $group_id) /** * 权限多维转化为二维 - * @param $cate 栏目 - * @param int $pid 父ID - * @param $rules 规则 + * @param $cate * @return array */ public function authNormal($cate) @@ -205,15 +224,13 @@ public function authNormal($cate) /** * 验证权限 + * @return true|void */ - public function checkNode() + public function roleAccess() { $cfg = config('funadmin'); - if ($this->requesturl === '/') { - $this->error(lang('Login again'), __u('login/index')); - } - if (!$this->isLogin()) { - $this->error(lang('Please Login First'), __u('backend/login/index')); + if ($this->requesturl === '/' || !$this->isLogin()) { + $this->error(lang('Please Login First')); } if (isset($cfg['auth_on']) && $cfg['auth_on'] == false) { return true; @@ -228,7 +245,10 @@ public function checkNode() ['href','=', $this->requesturl], ['module','=', $this->app] ]; - $this->hrefId = AuthRule::where($map)->where('status', 1)->value('id'); + $cache_key = 'get-rule-id-by-href-'.md5(json_encode($map)); + $this->hrefId = db_cache($cache_key,function()use($map){ + return AuthRule::where($map)->where('status', 1)->value('id'); + }); $hrefTemp = trim($this->requesturl, '/'); $menuid = 0; if (Str::endsWith($hrefTemp, '/index')) { @@ -236,7 +256,10 @@ public function checkNode() ['href', '=', substr($hrefTemp, 0, strlen($hrefTemp) - 6)], ['module', '=', $this->app] ]; - $menuid = AuthRule::where($where)->where('status', 1)->value('id'); + $cache_key = 'get-rule-id-by-href-'.md5(json_encode($where)); + $menuid = db_cache($cache_key,function()use($where){ + return AuthRule::where($where)->where('status', 1)->value('id'); + }); } if ($menuid) $this->hrefId = $menuid; //当前管理员权限 @@ -244,22 +267,25 @@ public function checkNode() //用户权限规则id $this->adminRules = array_unique(array_filter(explode(',', $rules))); if ($this->hrefId) { - if (!in_array($this->hrefId, $this->adminRules)) $this->error(lang('Permission Denied')); + if (!in_array($this->hrefId, $this->adminRules)) $this->error(lang('Permission Denied')); } } } /** * 前台权限节点 + * @param $url + * @return bool */ - public function authNode($url) + public function nodeAccess($url) { // 判断权限验证开关 - $cfg = Config::get('funadmin'); + $cfg = config('funadmin'); if (isset($cfg['auth_on']) && $cfg['auth_on'] == false) { return true; } - $url = parse_url($url)['path']; + $parse = parse_url($url); + $url = $parse['path']; if(Str::endsWith($url,'.' . config('view.view_suffix'))){ $this->requesturl = (string)$url; }else{ @@ -269,9 +295,12 @@ public function authNode($url) $this->requesturl = Str::substr($this->requesturl, 0, strlen($this->requesturl) - strlen(config('view.view_suffix')) - 1); } + if(!empty($parse['host']) && !empty($parse['scheme'])){ + $this->requesturl = $this->app.$this->requesturl; + } $this->requesturl = trim($this->requesturl, '/'); $requesturlArr = explode('/', $this->requesturl); - $this->app = array_shift($requesturlArr); + $app = array_shift($requesturlArr); $this->requesturl = implode('/', $requesturlArr); if ($this->requesturl === '/') return false; if (!$this->isLogin()) return false; @@ -279,16 +308,22 @@ public function authNode($url) if ($adminId != $cfg['superAdminId']) { $map = [ ['href', '=', $this->requesturl], - ['module', '=', $this->app] + ['module', '=', $app] ]; - $this->hrefId = AuthRule::where($map)->where('status', 1)->value('id'); + $cache_key = 'get-rule-id-by-module-href'.md5(json_encode($map)); + $this->hrefId = db_cache($cache_key,function()use($map){ + return AuthRule::where($map)->where('status', 1)->value('id'); + }); $menuid = 0; if (Str::endsWith($this->requesturl, '/index')) { $where[] = [ ['href', '=', substr($this->requesturl, 0, strlen($this->requesturl) - 6)], - ['module', '=', $this->app] + ['module', '=', $app] ]; - $menuid = AuthRule::where($where)->where('status', 1)->value('id'); + $cache_key = 'get-rule-id-by-module-href'.md5(json_encode($where)); + $menuid = db_cache($cache_key,function()use($where){ + return AuthRule::where($where)->where('status', 1)->value('id'); + }); } if ($menuid) $this->hrefId = $menuid; //当前管理员权限 @@ -313,79 +348,84 @@ public function menuhtml($cate, $force = true) Cache::delete('adminmenushtml' . session('admin.id')); } $list = $this->authMenuNode($cate); - $html = ''; $theme = syscfg('site', 'site_theme'); - if (empty($theme) || ($theme && in_array($theme,[1,2,5]))) { - foreach ($list as $key => $val) { - $html .= '

  • '; - $badge = ''; - if (strtolower($val['title']) === 'addon') { - $badge = 'new'; - } - if ($val['child'] and count($val['child']) > 0) { - $html .= ' ' . lang($val['title']) . '' . $badge . ''; - $html = $this->childmenuhtml($html, $val['child']); - } else { - $target = $val['target'] ? $val['target'] : '_self'; - $html .= ' ' . lang($val['title']) . '' . $badge . ''; - } - $html .= '
  • '; - } - } elseif ($theme == 3 || $theme ==4) { - $html = []; - $hide = ''; - $html['nav'] = ''; - $html['menu'] = ''; - $html['navm'] = '
  • - 请选择 -
    '; - foreach ($list as $key => $val) { - $laythis = $key == 0 ? 'layui-this' : ''; - $html['nav'] .= '
  • '; - $html['navm'] .= '
    ' . lang($val['title']) . '
    '; - $badge = ''; - if (strtolower($val['title']) === 'addon') { - $badge = 'new'; + $cache_key = 'adminmenushtml-'.$theme.md5(json_encode($list)); + $html = db_cache($cache_key,function()use($list,$theme){ + if (empty($theme) || ($theme && in_array($theme,[1,2,5]))) { + $html = ''; + foreach ($list as $key => $val) { + $html .= '
  • '; + $badge = ''; + if (strtolower($val['title']) === 'addon') { + $badge = 'new'; + } + if ($val['child'] and count($val['child']) > 0) { + $html .= ' ' . lang($val['title']) . '' . $badge . ''; + $html = $this->childmenuhtml($html, $val['child']); + } else { + $target = $val['target'] ? $val['target'] : '_self'; + $html .= ' ' . lang($val['title']) . '' . $badge . ''; + } + $html .= '
  • '; } + } elseif ($theme == 3 || $theme ==4) { + $html = []; $hide = ''; - if($theme==3){ - $hide = $theme<=3 && $key > 0 ? 'layui-hide' : ''; - } - if($theme==4){ - $hide = 'layui-hide'; - } - $html['menu'] .= ''; - } else { - $target = $val['target'] ? $val['target'] : '_self'; - $html['nav'] .= ' ' . lang($val['title']) . '' . $badge . ''; - $html['menu'] .= '
  • ' . lang($val['title']) . '' . $badge . '
  • '; + $html['nav'] .= '
  • '; } - $html['menu'] .= ''; - $html['nav'] .= ''; + $html['navm'] .= '
  • '; } - $html['navm'] .= '
  • '; - } + return $html; + }); return $html; + } /** + * 获取子菜单html * @param $html * @param $child * @return string - * 获取子菜单html */ public function childmenuhtml($html, $child, $type = 1) { @@ -441,7 +481,9 @@ public function isLogin() return false; } //判断是否同一时间同一账号只能在一个地方登录// 要是备份还原的话,这里会有点问题 - $me = AdminModel::find($admin['id']); + $me = db_cache('admin-user',function()use($admin){ + return AdminModel::find($admin['id']); + }); // if (!$me || $me['token'] != $admin['token']) { if (!$me) { $this->logout(); @@ -463,9 +505,10 @@ public function isLogin() /** * 根据用户名密码,验证用户是否能成功登陆 - * @param string $username - * @param string $password - * @return mixed + * @param $username + * @param $password + * @param $rememberMe + * @return true * @throws \Exception */ public function checkLogin($username, $password, $rememberMe) @@ -512,7 +555,8 @@ public function checkLogin($username, $password, $rememberMe) } /** - * 注销登录 + * 退出登录 + * @return boolean */ public function logout() { @@ -528,27 +572,40 @@ public function logout() /** - * 获取rules - * @param $groups - * @return void + * 获取用户组规则 + * @param mixed $groups + * @return string|null */ protected function getRules($groups) { if ($groups && in_array(1, explode(",", $groups))) { - $rules = AuthRule::where('status', 1)->cache('superAdmin', 24 * 3600)->column('id'); - $rules = implode(',', $rules); + $rules = db_cache('super-admin-auth-group-rules',function(){ + $rules = AuthRule::where('status', 1)->cache('superAdmin', 24 * 3600)->column('id'); + $rules = implode(',', $rules); + return $rules; + }); } else { //这一句有长度限制 // $rules = AuthGroupModel::where('id', 'in', $groups)->where('status', 1)->field('group_concat(rules)')->value('group_concat(rules)'); - $data = AuthGroupModel::where('id', 'in', $groups)->where('status', 1)->field('rules')->select(); - $rules = ''; - foreach ($data as $rule) { - $rules .=$rule['rules'].','; - } + $groups = is_string($groups)?explode(',', $groups):$groups; + $key = 'auth-group-rules-'.implode(',',$groups); + $rules = db_cache($key,function() use ($groups){ + $rules = AuthGroupModel::where('id', 'in', $groups)->where('status', 1)->column('rules');// 获取所有组的权限规则ID + $rules = implode(',', $rules);// 获取用户组权限规则ID + $rules = array_unique(explode(',', $rules));// 转换成数组并去重 + sort($rules);// 重新排序 + $rules = implode(',', $rules);//重新组合成字符串 + return $rules; + }); + } - $norules = AuthRule::where('auth_verify', 0)->column('id'); - $norules = $norules ? implode(',', $norules) : ''; - return $rules . ',' . $norules; + $norules = db_cache('no-rules',function(){ + $norules = AuthRule::where('auth_verify', 0)->column('id'); + $norules = $norules ? implode(',', $norules) : ''; + return $norules; + }); + $result = trim($rules . ',' . $norules, ','); + return $result !== '' ? $result : null; } //获取左侧主菜单树形结构 @@ -598,20 +655,25 @@ protected function authMenuNode($menu, $pid = 0, $rules = []) return $list; } - /** + /** * 获取所有子id + * @param mixed $pid + * @return string */ public function getAllIdsBypid($pid) { - $res = AuthRule::where('pid', $pid)->where('status', 1)->select(); - $str = ''; - if (!empty($res)) { - foreach ($res as $k => $v) { - $str .= "," . $v['id']; - $str .= $this->getAllIdsBypid($v['id']); + $res = db_cache('get-rule-ids-by-pid-'.$pid,function()use($pid){ + $res = AuthRule::where('pid', $pid)->where('status', 1)->select(); + $ids = []; + if (!empty($res)) { + foreach ($res as $v) { + $ids[] = $v['id']; + $ids = array_merge($ids, explode(',', $this->getAllIdsBypid($v['id']))); + } } - } - return $str; + return implode(',', array_filter($ids)); + }); + return $res; } diff --git a/app/backend/validate/TestCate.php b/app/backend/validate/TestCate.php deleted file mode 100644 index 1a2088338..000000000 --- a/app/backend/validate/TestCate.php +++ /dev/null @@ -1,31 +0,0 @@ - ['规则1','规则2'...] - * @var array - */ - protected $rule = []; - - /** - * 定义错误信息 - * 格式:'字段名.规则名' => '错误信息' - * @var array - */ - protected $message = []; - - /** - * 验证场景 - */ - protected $scene = [ - 'add' => [], - 'edit' => [], - ]; -} diff --git a/app/backend/view/addon/add.html b/app/backend/view/addon/add.html index 35e91cf98..067795179 100644 --- a/app/backend/view/addon/add.html +++ b/app/backend/view/addon/add.html @@ -1,5 +1,5 @@
    - {:form_input('name','text',['verify'=>'required','tips'=>'插件标识(唯一)'])} + {:form_input('app','text',['verify'=>'required','tips'=>'插件标识(唯一)'])} {:form_input('title','text',['verify'=>'required','tips'=>'插件标题'])} {:form_input('author','text',['verify'=>'required','tips'=>'插件作者'])} {:form_input('ver','number',['verify'=>'required','tips'=>'插件版本'])} diff --git a/app/backend/view/addon/config.html b/app/backend/view/addon/config.html index d161cb4a1..368b2ef68 100644 --- a/app/backend/view/addon/config.html +++ b/app/backend/view/addon/config.html @@ -6,79 +6,79 @@ {php}$name = "params[".$k."]";{/php} {switch name="$vo.type"} {case value="text"} - {:form_text($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} + {:Form::text($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} {/case} {case value="textarea"} - {:form_textarea($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} + {:Form::textarea($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} {/case} {case value="password"} - {:form_password($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} + {:Form::password($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} {/case} {case value="tel"} - {:form_tel($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} + {:Form::tel($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} {/case} {case value="email"} - {:form_email($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} + {:Form::email($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} {/case} {case value="range"} - {:form_range($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} + {:Form::range($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} {/case} {case value="color"} - {:form_color($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} + {:Form::color($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} {/case} {case value="tags"} - {:form_tags($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} + {:Form::tags($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} {/case} {case value="rate"} - {:form_rate($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} + {:Form::rate($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} {/case} {case value="icon"} - {:form_icon($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} + {:Form::icon($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} {/case} {case value="date"} - {:form_date($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} + {:Form::date($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} {/case} {case value="city"} - {:form_city($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} + {:Form::city($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} {/case} {case value="region"} - {:form_region($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} + {:Form::region($name,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} {/case} {case value="radio"} - {:form_radio($name,$vo.content,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} + {:Form::radio($name,$vo.content,['label'=>$vo.title,'tips'=>$vo.tips],$vo.value)} {/case} {case value="checkbox"} - {:form_checkbox($name, $vo.content,['label'=>$vo.title, 'verify' =>$vo.rule,'tips'=>$vo.tips,], $vo['value'])}; + {:Form::checkbox($name, $vo.content,['label'=>$vo.title, 'verify' =>$vo.rule,'tips'=>$vo.tips,], $vo['value'])}; {/case} {case value="select"} - {:form_select($name,$vo.content,['label'=>$vo.title,'verify'=>$vo.rule,'tips'=>$vo.tips,'search'=>1] ,[],$vo.value)} + {:Form::select($name,$vo.content,['label'=>$vo.title,'verify'=>$vo.rule,'tips'=>$vo.tips,'search'=>1] ,[],$vo.value)} {/case} {case value="selects"} - {:form_select($name,$vo.content,['label'=>$vo.title,'multiple'=>1,'verify'=>$vo.rule,'tips'=>$vo.tips,'search'=>1] ,[],$vo.value)} + {:Form::select($name,$vo.content,['label'=>$vo.title,'multiple'=>1,'verify'=>$vo.rule,'tips'=>$vo.tips,'search'=>1] ,[],$vo.value)} {/case} {case value="selectpage"} - {:form_select($name,$vo.content,['label'=>$vo.title,'multiple'=>$vo.multiple,'int'=>$vo.int?$vo.int:'','data'=>$vo.data?$vo.data:'','filed'=>$vo.filed?$vo.filed:'','primarykey'=>$vo.primarykey?$vo.primarykey:'','tips'=>$vo.tips] ,[],$vo.value)} + {:Form::select($name,$vo.content,['label'=>$vo.title,'multiple'=>$vo.multiple,'int'=>$vo.int?$vo.int:'','data'=>$vo.data?$vo.data:'','filed'=>$vo.filed?$vo.filed:'','primarykey'=>$vo.primarykey?$vo.primarykey:'','tips'=>$vo.tips] ,[],$vo.value)} {/case} {case value="autocomplete"} - {:form_select($name,$vo.content,['label'=>$vo.title] ,[],$vo.value)} + {:Form::select($name,$vo.content,['label'=>$vo.title] ,[],$vo.value)} {/case} {case value="xmselect"} - {:form_xmselect($name,$vo.content,['label'=>$vo.title] ,[],$vo.value)} + {:Form::xmselect($name,$vo.content,['label'=>$vo.title] ,[],$vo.value)} {/case} {case value="image"} - {:form_upload($name,['label'=>$vo.title,'tips'=>$vo.tips,'verify'=>$vo.rule,'type'=>'radio','num'=>'1','mime'=>'images'],$vo.value)} + {:Form::upload($name,['label'=>$vo.title,'tips'=>$vo.tips,'verify'=>$vo.rule,'type'=>'radio','num'=>'1','mime'=>'images'],$vo.value)} {/case} {case value="images"} - {:form_upload($name,['label'=>$vo.title,'tips'=>$vo.tips,'verify'=>$vo.rule,'type'=>'checkbox','num'=>$vo.num,'mime'=>'images'],$vo.value)} + {:Form::upload($name,['label'=>$vo.title,'tips'=>$vo.tips,'verify'=>$vo.rule,'type'=>'checkbox','num'=>$vo.num,'mime'=>'images'],$vo.value)} {/case} {case value="file"} - {:form_upload($name,['label'=>$vo.title,'tips'=>$vo.tips,'verify'=>$vo.rule,'type'=>'radio','num'=>$vo.num,'mime'=>'*'],$vo.value)} + {:Form::upload($name,['label'=>$vo.title,'tips'=>$vo.tips,'verify'=>$vo.rule,'type'=>'radio','num'=>$vo.num,'mime'=>'*'],$vo.value)} {/case} {case value="files"} - {:form_upload($name,['label'=>$vo.title,'tips'=>$vo.tips,'verify'=>$vo.rule,'type'=>'checkbox','num'=>$vo.num,'mime'=>'*'],$vo.value,)} + {:Form::upload($name,['label'=>$vo.title,'tips'=>$vo.tips,'verify'=>$vo.rule,'type'=>'checkbox','num'=>$vo.num,'mime'=>'*'],$vo.value,)} {/case} {case value="editor"} - {:form_editor($name,['label'=>$vo.title,'tips'=>$vo.tips,'verify'=>$vo.rule])} + {:Form::editor($name,['label'=>$vo.title,'tips'=>$vo.tips,'verify'=>$vo.rule])} {/case} {case value="array"} {php} @@ -89,7 +89,7 @@ $list['value'][] = $v; } {/php} - {:form_arrays($name,$list,['label'=>$vo.title,'tips'=>$vo.tips,'verify'=>$vo.rule])} + {:Form::arrays($name,$list,['label'=>$vo.title,'tips'=>$vo.tips,'verify'=>$vo.rule])} {/case} {case value="arrays"} {php} @@ -100,11 +100,11 @@ $list['value'][] = $v; } {/php} - {:form_arrays($name,$list,['label'=>$vo.title,'tips'=>$vo.tips,'verify'=>$vo.rule])} + {:Form::arrays($name,$list,['label'=>$vo.title,'tips'=>$vo.tips,'verify'=>$vo.rule])} {/case} {/switch} {/foreach} - {:form_submitbtn()} + {:Form::submit()}
    diff --git a/app/backend/view/auth/admin/add.html b/app/backend/view/auth/admin/add.html index e959e30be..adaeeb465 100644 --- a/app/backend/view/auth/admin/add.html +++ b/app/backend/view/auth/admin/add.html @@ -53,6 +53,9 @@

    {$formData.realname}

    {:form_input('username','text',['verify'=>'required','disabled'=>true,])} + {if request()->action()=='edit'} + {:form_select('group_id',$authGroup,['verify'=>'required','multiple'=>1,'search'=>1],'id,ltitle',$formData['group_id']??'')} + {/if} {:form_input('realname','text',['verify'=>'required','tips'=>'realname'] )} {:form_input('password','password',['verify'=>'','tips'=>'If you do not fill in, you will not change the password'])} {:form_upload('avatar',['mime'=>'images'],$formData['avatar']??'')} diff --git a/app/backend/view/auth/admin/password.html b/app/backend/view/auth/admin/password.html index 76c7965bf..3f82f7f48 100644 --- a/app/backend/view/auth/admin/password.html +++ b/app/backend/view/auth/admin/password.html @@ -1,5 +1,6 @@ - {if $id} +
    + {if $id} {:form_input('oldpassword','password',['label'=>'NewPasswd','verify'=>'required|pass'])} {:form_input('password','password',['label'=>'ReNewPasswd','verify'=>'required|pass'])} {else /} @@ -11,4 +12,5 @@ {else/} {:form_submitbtn()} {/if} +
    \ No newline at end of file diff --git a/app/backend/view/auth/auth/add.html b/app/backend/view/auth/auth/add.html index c4d37012a..4a8d97b32 100644 --- a/app/backend/view/auth/auth/add.html +++ b/app/backend/view/auth/auth/add.html @@ -3,7 +3,7 @@ 1、控制器/方法 例如 Wechat/index 或者多级 wechat.sys/index,注意大小写
    - {:form_xmselect('pid',[],['url'=>__u('index'),'verify'=>'',"radio"=>1,'tree'=>true],['title','id'],$formData['pid']??'')} + {:form_xmselect('pid',[],['url'=>__u('index'),'verify'=>'',"radio"=>1,'tree'=>true],['id','title'],$formData['pid']??'')} {:form_input('title','text',['verify'=>'required','label'=>"Name"])} {:form_input('href','text',['verify'=>'required','tips'=>'Controller/Action'])} {:form_input('module','text',['verify'=>'required','tips'=>'module (backend || appname)'],'backend')} diff --git a/app/backend/view/auth/auth/child.html b/app/backend/view/auth/auth/child.html index 59af00a5d..ebee94398 100644 --- a/app/backend/view/auth/auth/child.html +++ b/app/backend/view/auth/auth/child.html @@ -4,7 +4,7 @@
    - +
    getDataPropAttr($v, $val, $options)} class="layui-select-url selectcx{$k} layui-select {$v} {$this->getClass($options)}" > +
    @@ -753,13 +779,8 @@ public function selectcx($name = 'province_id,city_id,area_id', $select = [], $o } $options['attr'] = $attr; - if(!isset($options['create'])){ - $options['create'] = true; - } $value = implode(',',$value); $options['filter'] = $options['filter']??'cxselect'; - $verify = $options['verify']??''; - unset($options['verify']); $str = << {$this->label($name, $options)}
    getDataPropAttr(implode(',',$fields), $value, $options)}> @@ -820,33 +841,8 @@ public function select($name = '', $select = [], $options = [], $attr = [], $val if(isset($options['multiple'])){ return $this->selects($name, $select , $options, $attr, $value); } - list($name, $id) = $this->getNameId($name, $options); - $value = $value??''; - $op = ''; - if ($select) { - $attr = is_array($attr)?$attr:explode(',',$attr); - $attr = array_filter($attr); - $value = is_array($value)?$value:explode(',',$value); - foreach ($select as $k => $v) { - $selected = ''; - if (is_array($v) && !empty($attr) && in_array($v[$attr[0]], $value)) { - $selected = 'selected'; - } - if (in_array($k, $value) && empty($attr)) { - $selected = 'selected'; - } - if (!is_array($v)) { - $op .= ''; - }elseif (is_array($v) && !empty($attr)) { - $op .= ''; - } - } - } - if (isset($options['default'])) { - $default = $this->__($options['default']); - } else { - $default = $this->__('Select'); - } + $options['selectList'] = $select; + $options['filter'] = $options['filter']??'select'; $attr = is_array($attr) ? implode(',', $attr) : $attr; $options['attr'] = $attr; if(!isset($options['search'])){ @@ -855,15 +851,10 @@ public function select($name = '', $select = [], $options = [], $attr = [], $val if(!isset($options['create'])){ $options['create'] = true; } - if(!empty($options['url'])){ - $options['filter'] = 'select'; - } $str = << {$this->label($name, $options)}
    {$this->tips($options)}
    @@ -920,15 +911,8 @@ public function selects($name = '', $select = [], $options = [], $attr = [], $va if(!isset($options['create'])){ $options['create'] = true; } - $options['data'] = json_encode($select,JSON_UNESCAPED_UNICODE); - $str = << {$this->label($name, $options)} -
    - readonlyOrdisabled($options)} {$this->getDataPropAttr($name, $value, $options)} placeholder="" /> - {$this->tips($options)} -
    -
    -EOF; + $options['selectList'] = $select; + $str = $this->input($name, 'text', $options,$value); return $str; } @@ -946,9 +930,10 @@ public function xmselect($name = '', $select = [], $options = [], $attr = [], $v list($name, $id) = $this->getNameId($name, $options); $select = ArrayHelper::getArray($select); $op = ''; - $op .= " data-data='" . json_encode($select, JSON_UNESCAPED_UNICODE) . "'"; + $op .= " data-select-list='" . json_encode($select, JSON_UNESCAPED_UNICODE) . "'"; $attr = is_array($attr) ? implode(',', array_filter($attr)) : $attr; $value = is_array($value) ? implode(',',$value) : $value; + $options['selectList'] = $options['selectList'] ?? $select; $options['attr'] = $options['attr'] ?? $attr; $options['lang'] = $options['lang'] ?? ''; $options['tips'] = $options['tips'] ?? ''; @@ -1006,7 +991,7 @@ public function selectpage(string $name, array $lists = [], array $options = [], $lists = ArrayHelper::getArray($lists); list($name, $id) = $this->getNameId($name, $options); $options['filter'] = 'selectPage'; - $options['data'] = empty($lists) ? '' : json_encode($lists,JSON_UNESCAPED_UNICODE); + $options['selectList'] = empty($lists) ? '' : $lists; $options['field'] = $options['field'] ?? 'title'; $options['primaryKey'] = $options['field'] ?? 'id'; $options['multiple'] = $options['multiple'] ?? ''; @@ -1109,7 +1094,7 @@ public function date($name = '', $options = [], $value = '')
    {$this->label($name, $options)}
    - getDataPropAttr($name, $value, $options)} class="layui-input {$this->getClass($options)} {$this->readonlyOrdisabled($options)}" type="text" /> + getDataPropAttr($name, $value, $options)} class="layui-input {$this->getClass($options)}" {$this->readonlyOrdisabled($options)} type="text" />
    @@ -1162,6 +1147,7 @@ public function editor($name = 'container', $options = [], $value = '') $options['height'] = $options['height'] ?? '400px'; $options['url'] = $options['url'] ?? ''; $options['editor'] = $options['editor'] ?? (syscfg('upload', 'upload_editor') ?: 'tinymce'); + $options['driver'] = $options['driver']?? 'local'; $options['filter'] = 'editor'; if ($options['editor'] == 'tinymce') { // tinyedit @@ -1236,6 +1222,7 @@ public function upload($name = 'avatar', $options = [], $value = '') if (empty($options['num'])) $options['num'] = 1; if (!empty($options['num']) && $options['num'] == '*') $options['num'] = 100; if (empty($options['path'])) $options['path'] = 'upload'; //上传路劲 + if (empty($options['driver'])) $options['driver'] = 'local'; $css = $options['css'] ?? 'display:inline-block;'; $li = ''; $croper_container = ''; @@ -1409,11 +1396,11 @@ public function submitbtn($reset = true, $options = []) } $str = << -
    +
    {$reset} -
    +
    EOF; return $str; @@ -1457,13 +1444,13 @@ public function style(string $style, $options = []) /** * JS - * @param $name - * @param $options + * @param string|array $name JS文件路径 + * @param array $options 配置选项 * @return string */ public function js($name = [], $options = []) { - if(Str::contains($name,'')){ + if(is_string($name) && Str::contains($name,'')){ return $name; } if (is_string($name)) { @@ -1480,13 +1467,13 @@ public function js($name = [], $options = []) /** * css - * @param $name - * @param $options + * @param string|array $name CSS文件路径 + * @param array $options 配置选项 * @return string */ public function link($name = [], $options = []) { - if(Str::contains($name,'getExtend($options); - } else { - $options['id'] = $options['id'] ?? $name; - $options['name'] = $options['formname'] ?? ($options['fromName'] ?? $name); - $options['label'] = $options['label'] ?? $name; - $options['tips'] = $options['tips'] ?? ''; - $options['filter'] = $options['filter'] ?? $name; - foreach ($options as $key => $val) { - switch ($key) { - case 'class': - case 'tips': - case 'css': - case 'label': - break; - case 'placeholder': - $attr .= $key . '="' . $this->__($val) . '" '; - break; - case 'verify': - $attr .= $this->layverify($options); - break; - case 'filter': - $attr .= $this->layfilter($options); - break; - case 'step': - $attr .= $this->laystep($options); - break; - case 'affix': - $attr .= $this->layaffix($options); - break; - case 'autocomplete': - $attr .= $this->layautocomplete($options); - break; - case 'submit': - $attr .= $this->laysubmit($options); - break; - case 'ignore': - $attr .= $this->layignore($options); - break; - case 'precision': - $attr .= $this->layprecision($options); - break; - case 'style': - $attr .= $this->getStyle($options); - case 'readonly': - case 'disabled': - $attr .= $this->readonlyOrdisabled($options); - break; - case 'search': - $attr .= $this->laysearch($options); - break; - case 'creatable': - case 'create': - $attr .= $this->laycreatable($options); - break; - case 'skin': - $attr .= $this->layskin($options); - break; - case 'value': - $attr .= $key . "='" . $this->entities($val) . "' data-" . $key . "='" . $this->entities($val) . "' "; - break; - case 'attr': - if (is_object($val) || is_array($val)) { - $val = (array)$val; - $val = implode(',', $val); - } - $attr .= " data-" . $key . "='" . $val . "' "; - break; - default: - if (is_object($val) || is_array($val)) { - $val = (array)$val; - $val = $val[$name] ?? json_encode($val, JSON_UNESCAPED_UNICODE); - } - $attr .= " data-" . $key . "='" . $val . "' "; - break; + // 设置默认值 + $options['id'] = $options['id'] ?? $name; + $options['name'] = $options['formname'] ?? ($options['fromName'] ?? $name); + $options['label'] = $options['label'] ?? $name; + $options['tips'] = $options['tips'] ?? ''; + $options['filter'] = $options['filter'] ?? $name; + + // 特殊属性处理映射 + $specialAttributes = [ + 'verify', + 'filter', + 'step', + 'affix', + 'autocomplete', + 'submit', + 'ignore', + 'precision', + 'search', + 'creatable', + 'create', + 'skin', + 'style', + 'event', + 'create', + 'search', + 'wheel', + 'step-strictly' + ]; + + // 需要跳过的属性列表 + $skipAttributes = ['class','style', 'tips', 'css', 'label', 'outclass']; + foreach ($options as $key => $val) { + // 跳过不需要处理的属性 + if (in_array($key, $skipAttributes)) { + unset($options[$key]); + continue; + } + // 处理特殊属性 + if (in_array( $key, $specialAttributes)) { + if($key == 'step' || $key == 'min' || $key == 'max'){ + $attr .= $key.'="'.$val.'" '; + }else{ + $attr .= $this->layAttr($key,$val); } + unset($options[$key]); + } + // 处理placeholder属性 + elseif ($key === 'placeholder') { + $attr .= $key . '="' . $this->__($val) . '" '; + unset($options[$key]); } + // 处理value属性 + elseif ($key === 'value') { + $attr .= $key . "='" . $this->entities($val) . "' data-" . $key . "='" . $this->entities($val) . "' "; + } else if(!empty($val) && ($key === 'disabled' || $key === 'readonly')){ + $attr .= $key.'="'.$val.'"'; + } + } + // 处理剩余选项数据 + if (!empty($options)) { + $attr .= $this->getOptionsData($options); } + return $attr; } @@ -1870,7 +1859,26 @@ protected function getDataPropAttr($name = '', $value = '', $options = []) $str .= $this->getOptionsAttr($name, $options); return $str; } - + /** + * 获取data属性 + * @param $name + * @param $value + * @param $options + * @return string + */ + protected function getOptionsData($options = []) + { + if (empty($options)) { + return ''; + } + // 使用JSON_UNESCAPED_UNICODE确保中文不被转义,并添加错误处理 + $jsonData = json_encode($options, JSON_UNESCAPED_UNICODE); + if (json_last_error() !== JSON_ERROR_NONE) { + // 如果JSON编码失败,记录错误并返回空字符串 + return ''; + } + return " options='".htmlspecialchars($jsonData, ENT_QUOTES, 'UTF-8')."'"; + } /** * 获取值 * @param $name diff --git a/extend/fun/helper/HttpHelper.php b/extend/fun/helper/HttpHelper.php index 85d2592c9..a411248ee 100644 --- a/extend/fun/helper/HttpHelper.php +++ b/extend/fun/helper/HttpHelper.php @@ -14,6 +14,9 @@ namespace fun\helper; use GuzzleHttp\RequestOptions; use GuzzleHttp\Client; +use GuzzleHttp\Cookie\CookieJar; +use GuzzleHttp\Cookie\SetCookie; +use GuzzleHttp\Cookie\CookieJarInterface; class HttpHelper { @@ -21,26 +24,28 @@ class HttpHelper * 发送一个POST请求 * @param string $url 请求URL * @param array $params 请求参数 + * @param array $header 请求头 * @param array $options 扩展参数 - * @return mixed|string + * @param array $cookies Cookie参数 + * @return array 返回请求结果 */ public static function post($url, $params = [], $header = [], $options = [], $cookies = []) { - $req = self::sendRequest($url, $params, 'POST', $header, $options, $cookies); - return $req['ret'] ? $req['msg'] : ''; + return self::sendRequest($url, $params, 'POST', $header, $options, $cookies); } /** * 发送一个GET请求 * @param string $url 请求URL * @param array $params 请求参数 + * @param array $header 请求头 * @param array $options 扩展参数 - * @return mixed|string + * @param array $cookies Cookie参数 + * @return array 返回请求结果 */ public static function get($url, $params = [], $header = [], $options = [], $cookies = []) { - $req = self::sendRequest($url, $params, 'GET', $header, $options, $cookies); - return $req['ret'] ? $req['msg'] : ''; + return self::sendRequest($url, $params, 'GET', $header, $options, $cookies); } /** @@ -48,22 +53,102 @@ public static function get($url, $params = [], $header = [], $options = [], $coo * @param string $url 请求的链接 * @param mixed $params 传递的参数 * @param string $method 请求的方法 + * @param array $header 请求头 * @param mixed $options CURL的参数 + * @param array $cookies Cookies数组,格式可以是: + * ['name1' => 'value1', 'name2' => 'value2'] + * 或包含完整配置的数组: + * ['name1' => ['Name' => 'name1', 'Value' => 'value1', 'Domain' => 'example.com']] * @return array + * + * @example + * // 基本使用 + * HttpHelper::sendRequest('https://example.com', ['param' => 'value']); + * + * // 带Cookies的GET请求 + * HttpHelper::sendRequest('https://example.com', [], 'GET', [], [], ['token' => '12345']); + * + * // 带详细Cookie设置 + * $cookies = [ + * 'token' => [ + * 'Name' => 'token', + * 'Value' => '12345', + * 'Domain' => 'example.com', + * 'Secure' => true, + * 'HttpOnly' => true, + * 'Expires' => time() + 3600 + * ] + * ]; + * HttpHelper::sendRequest('https://example.com', [], 'GET', [], [], $cookies); */ public static function sendRequest($url, $params = [], $method = 'POST', $header = [], $options = [], $cookies = []) { + if (empty($url)) { + return ['ret' => false, 'msg' => 'URL cannot be empty', 'data' => []]; + } + + $method = strtoupper($method); + $requestOptions = []; + + // Handle different request types + if ($method === 'GET') { + $requestOptions['query'] = $params; + } elseif (isset($header['Content-Type']) && strpos($header['Content-Type'], 'application/json') !== false) { + $requestOptions['json'] = $params; + } else { + $requestOptions['form_params'] = $params; + } + try { - $client = self::getClient($options, $header,$cookies); - $response = $client->request($method, $url, strtoupper($method)=='GET' ? ['query' => $params] : ['form_params'=>$params])->getBody()->getContents(); - if (!empty($response)) { - return ['ret' => true, 'msg' => $response]; + $client = self::getClient($options, $header, $cookies); + $response = $client->request($method, $url, $requestOptions); + $statusCode = $response->getStatusCode(); + $contents = $response->getBody()->getContents(); + // 默认情况下只返回响应结果,不直接判断为失败 + // 上层应用可以根据status_code自行判断 + // Check for successful status codes (2xx) + if ($statusCode >= 200 && $statusCode < 300) { + return [ + 'ret' => true, + 'msg' => $contents, + 'data' => $contents, + 'status_code' => $statusCode + ]; + } else { + return [ + 'ret' => false, + 'msg' => "HTTP Error: $statusCode", + 'data' => $contents, + 'status_code' => $statusCode + ]; } + } catch (\GuzzleHttp\Exception\ConnectException $e) { + return [ + 'ret' => false, + 'msg' => 'Connection error: ' . $e->getMessage(), + 'data' => [], + 'exception' => get_class($e) + ]; + } catch (\GuzzleHttp\Exception\RequestException $e) { + $response = $e->hasResponse() ? $e->getResponse() : null; + $statusCode = $response ? $response->getStatusCode() : 0; + $contents = $response ? $response->getBody()->getContents() : ''; + + return [ + 'ret' => false, + 'msg' => $e->getMessage(), + 'data' => $contents, + 'status_code' => $statusCode, + 'exception' => get_class($e) + ]; } catch (\Throwable $e) { - return ['ret' => false, 'msg' => $e->getMessage()]; + return [ + 'ret' => false, + 'msg' => $e->getMessage(), + 'data' => [], + 'exception' => get_class($e) + ]; } - return ['ret' => false, 'msg' => $response]; - } /** @@ -78,149 +163,439 @@ public static function sendRequest($url, $params = [], $method = 'POST', $header */ public static function sendAsyncRequest($url, $params = [], $method = 'POST', $header = [], $options = [], $cookies = []) { + if (empty($url)) { + return ['ret' => false, 'msg' => 'URL cannot be empty', 'data' => []]; + } + + $method = strtoupper($method); + $requestOptions = []; + + // Handle different request types + if ($method === 'GET') { + $requestOptions['query'] = $params; + } elseif (isset($header['Content-Type']) && strpos($header['Content-Type'], 'application/json') !== false) { + $requestOptions['json'] = $params; + } else { + $requestOptions['form_params'] = $params; + } + try { - $client = self::getClient($options, $header,$cookies); - $promise = $client->requestAsync($method, $url, strtoupper($method)=='GET' ? ['query' => $params] : ['form_params'=>$params])->getBody()->getContents(); - $promise->then(function ($response) { - if ($response->getStatusCode() == 200) { - return ['ret' => true, 'msg' => $response]; + $client = self::getClient($options, $header, $cookies); + $promise = $client->requestAsync($method, $url, $requestOptions); + + $promise->then( + function ($response) { + $statusCode = $response->getStatusCode(); + $contents = $response->getBody()->getContents(); + + return [ + 'ret' => true, + 'msg' => $contents, + 'data' => $contents, + 'status_code' => $statusCode + ]; + }, + function ($exception) { + if ($exception instanceof \GuzzleHttp\Exception\RequestException && $exception->hasResponse()) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + $contents = $response->getBody()->getContents(); + + return [ + 'ret' => false, + 'msg' => $exception->getMessage(), + 'data' => $contents, + 'status_code' => $statusCode, + 'exception' => get_class($exception) + ]; + } + + return [ + 'ret' => false, + 'msg' => $exception->getMessage(), + 'data' => [], + 'exception' => get_class($exception) + ]; } - }); + ); + + return $promise; } catch (\Throwable $e) { - return ['ret' => false, 'msg' => $e->getMessage()]; + return [ + 'ret' => false, + 'msg' => $e->getMessage(), + 'data' => [], + 'exception' => get_class($e) + ]; } - return ['ret' => false, 'msg' => $response];; } /** - * 发送文件到客户端 - * @param string $file - * @param bool $delaftersend - * @param bool $exitaftersend + * 发送文件到客户端浏览器 + * @param string $file 文件路径 + * @param bool $delaftersend 发送后是否删除文件 + * @param bool $exitaftersend 发送后是否退出脚本 + * @return array 发送结果,包含成功状态和消息 */ public static function sendToBrowser($file, $delaftersend = true, $exitaftersend = true) { - if (file_exists($file) && is_readable($file)) { + if (!file_exists($file) || !is_readable($file)) { + return ['ret' => false, 'msg' => '文件不存在或不可读: ' . $file]; + } + + try { + $filename = basename($file); + $filesize = filesize($file); + $mimeType = mime_content_type($file) ?: 'application/octet-stream'; + + // 清除之前的输出缓冲 + while (ob_get_level()) { + ob_end_clean(); + } + + // 设置正确的头信息 header('Content-Description: File Transfer'); - header('Content-Type: application/octet-stream'); - header('Content-Disposition: attachment;filename = ' . basename($file)); + header('Content-Type: ' . $mimeType); + header('Content-Disposition: attachment; filename="' . $filename . '"'); header('Content-Transfer-Encoding: binary'); header('Expires: 0'); - header('Cache-Control: must-revalidate, post-check = 0, pre-check = 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); header('Pragma: public'); - header('Content-Length: ' . filesize($file)); - ob_clean(); - flush(); + header('Content-Length: ' . $filesize); + + // 发送文件 readfile($file); - if ($delaftersend) { - unlink($file); + + // 发送后删除文件 + if ($delaftersend && is_writable($file)) { + @unlink($file); } + + // 发送后退出脚本 if ($exitaftersend) { exit; } + + return ['ret' => true, 'msg' => '文件发送成功']; + } catch (\Throwable $e) { + return ['ret' => false, 'msg' => '发送文件错误: ' . $e->getMessage()]; } } /** - * 下载并保存 - * @param $url - * @param $savePath - * @param string $params - * @param array $header - * @param int $timeout - * @return false|mixed + * 发送一个POST请求,参数为JSON格式 + * @param string $url 请求URL + * @param array $data 请求数据,将被转为JSON + * @param array $header 请求头 + * @param array $options 扩展参数 + * @param array $cookies Cookie参数 + * @return array 返回请求结果 + */ + public static function postJson($url, $data = [], $header = [], $options = [], $cookies = []) + { + $header = array_merge(['Content-Type' => 'application/json'], $header); + return self::sendRequest($url, $data, 'POST', $header, $options, $cookies); + } + + /** + * 发送一个PUT请求,参数为JSON格式 + * @param string $url 请求URL + * @param array $data 请求数据,将被转为JSON + * @param array $header 请求头 + * @param array $options 扩展参数 + * @param array $cookies Cookie参数 + * @return array 返回请求结果 + */ + public static function putJson($url, $data = [], $header = [], $options = [], $cookies = []) + { + $header = array_merge(['Content-Type' => 'application/json'], $header); + return self::sendRequest($url, $data, 'PUT', $header, $options, $cookies); + } + + /** + * 下载并保存文件 + * @param string $url 要下载的文件URL + * @param string $filename 保存的文件名,为空时自动生成 + * @param int $timeout 超时时间(秒) + * @param array $header 请求头 + * @param array $options 请求选项 + * @return array 下载结果,包含成功状态、消息和文件路径 */ - public static function download($url, $filename = "", $timeout = 60) + public static function download($url, $filename = "", $timeout = 60, $header = [], $options = []) { + if (empty($url)) { + return ['ret' => false, 'msg' => '下载URL不能为空', 'data' => null]; + } + + // 生成默认文件名 if (empty($filename)) { - $filename = public_path() . 'temp' . DS . pathinfo($url, PATHINFO_BASENAME); + $tempDir = public_path() . 'temp' . DIRECTORY_SEPARATOR; + if (!is_dir($tempDir) && !mkdir($tempDir, 0755, true)) { + return ['ret' => false, 'msg' => '无法创建临时目录', 'data' => null]; + } + $filename = $tempDir . pathinfo($url, PATHINFO_BASENAME); } + + // 创建目录 $path = dirname($filename); if (!is_dir($path) && !mkdir($path, 0755, true)) { - return false; + return ['ret' => false, 'msg' => '无法创建目录: ' . $path, 'data' => null]; } - $client = self::getClient(); - $file = fopen($filename, 'w+'); - $httpClient = new Client(); - $response = $httpClient->get($url, [RequestOptions::SINK => $file]); - if ($response->getStatusCode() === 200) { - return $filename; + + // 设置下载选项 + $downloadOptions = array_merge([ + 'timeout' => $timeout, + 'verify' => false, + 'headers' => $header, + 'sink' => $filename + ], $options); + + try { + $client = new Client(); + $response = $client->get($url, $downloadOptions); + + $statusCode = $response->getStatusCode(); + + if ($statusCode >= 200 && $statusCode < 300) { + return [ + 'ret' => true, + 'msg' => '文件下载成功', + 'data' => [ + 'filename' => $filename, + 'size' => filesize($filename), + 'mime' => $response->getHeaderLine('Content-Type') + ] + ]; + } else { + return [ + 'ret' => false, + 'msg' => "HTTP错误: $statusCode", + 'data' => null + ]; + } + } catch (\GuzzleHttp\Exception\ConnectException $e) { + return [ + 'ret' => false, + 'msg' => '连接错误: ' . $e->getMessage(), + 'data' => null + ]; + } catch (\GuzzleHttp\Exception\RequestException $e) { + return [ + 'ret' => false, + 'msg' => '请求错误: ' . $e->getMessage(), + 'data' => null + ]; + } catch (\Throwable $e) { + return [ + 'ret' => false, + 'msg' => '下载错误: ' . $e->getMessage(), + 'data' => null + ]; } - return false; } - /** php 接收流文件 - * @param String $file 接收后保存的文件名 - * @return boolean + /** + * 接收流文件 + * @param string $receiveFile 接收后保存的文件名 + * @return array 接收结果,包含成功状态、消息和文件信息 */ public static function receiveStreamFile($receiveFile) { - $streamData = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : ''; - if (empty($streamData)) { - $streamData = file_get_contents('php://input'); + if (empty($receiveFile)) { + return ['ret' => false, 'msg' => '目标文件路径不能为空', 'data' => null]; } - if ($streamData != '') { - $ret = file_put_contents($receiveFile, $streamData, true); - } else { - $ret = false; + + // 确保目录存在 + $path = dirname($receiveFile); + if (!is_dir($path) && !mkdir($path, 0755, true)) { + return ['ret' => false, 'msg' => '无法创建目录: ' . $path, 'data' => null]; + } + + try { + // 读取输入流 + $streamData = file_get_contents('php://input'); + + if (empty($streamData)) { + return ['ret' => false, 'msg' => '接收到的数据为空', 'data' => null]; + } + + // 写入文件 + $bytes = file_put_contents($receiveFile, $streamData, LOCK_EX); + + if ($bytes === false) { + return ['ret' => false, 'msg' => '写入文件失败', 'data' => null]; + } + + return [ + 'ret' => true, + 'msg' => '文件接收成功', + 'data' => [ + 'filename' => $receiveFile, + 'size' => $bytes + ] + ]; + } catch (\Throwable $e) { + return ['ret' => false, 'msg' => '接收流文件错误: ' . $e->getMessage(), 'data' => null]; } - return $ret; } - /** php 发送流文件 - * @param String $url 接收的路径 - * @param String $file 要发送的文件 - * @return boolean + /** + * 发送流文件 + * @param string $url 接收的URL路径 + * @param string $file 要发送的文件路径 + * @param array $headers 请求头 + * @return array 发送结果,包含成功状态、消息和响应数据 */ - public static function sendStreamFile($url, $file) + public static function sendStreamFile($url, $file, $headers = []) { - if (file_exists($file)) { - $opts = array( - 'http' => array( - 'method' => 'POST', - 'header' => 'content-type:application/x-www-form-urlencoded', - 'content' => file_get_contents($file) - ) - ); - $context = stream_context_create($opts); - $response = file_get_contents($url, false, $context); - $ret = json_decode($response, true); - return $ret['success']; - } else { - return false; + if (empty($url)) { + return ['ret' => false, 'msg' => '目标URL不能为空', 'data' => null]; + } + + if (empty($file) || !file_exists($file)) { + return ['ret' => false, 'msg' => '文件不存在: ' . $file, 'data' => null]; + } + + try { + // 准备文件内容 + $fileContent = file_get_contents($file); + + // 设置默认请求头 + $defaultHeaders = [ + 'Content-Type' => 'application/octet-stream', + 'Content-Length' => filesize($file), + 'Content-Disposition' => 'attachment; filename="' . basename($file) . '"' + ]; + + // 合并请求头 + $headers = array_merge($defaultHeaders, $headers); + + // 使用GuzzleHttp发送请求 + $client = new Client(); + $response = $client->post($url, [ + 'headers' => $headers, + 'body' => $fileContent, + 'verify' => false + ]); + + $statusCode = $response->getStatusCode(); + $contents = $response->getBody()->getContents(); + + if ($statusCode >= 200 && $statusCode < 300) { + return [ + 'ret' => true, + 'msg' => '文件发送成功', + 'data' => [ + 'response' => $contents, + 'status_code' => $statusCode + ] + ]; + } else { + return [ + 'ret' => false, + 'msg' => "HTTP错误: $statusCode", + 'data' => $contents + ]; + } + } catch (\GuzzleHttp\Exception\ConnectException $e) { + return ['ret' => false, 'msg' => '连接错误: ' . $e->getMessage(), 'data' => null]; + } catch (\GuzzleHttp\Exception\RequestException $e) { + return ['ret' => false, 'msg' => '请求错误: ' . $e->getMessage(), 'data' => null]; + } catch (\Throwable $e) { + return ['ret' => false, 'msg' => '发送流文件错误: ' . $e->getMessage(), 'data' => null]; } } - /** * 获取访问客户端 * @param array $options * @param array $header - * @return mixed + * @param array $cookies + * @return \GuzzleHttp\Client */ - private static function getClient(array $options = [], array $header = [], array $cookies= []) + private static function getClient(array $options = [], array $header = [], array $cookies = []) { - if (empty($options)) { - $options = [ - 'timeout' => 60, - 'connect_timeout' => 60, - 'verify' => false, - 'http_errors' => false, - 'headers' => [ - 'X-REQUESTED-WITH' => 'XMLHttpRequest', - 'Referer' => dirname(request()->url()), - ] - ]; - } + $defaultOptions = [ + 'timeout' => 30, // Default timeout reduced to 30 seconds + 'connect_timeout' => 10, // Faster connection timeout for better UX + 'verify' => false, // Disable SSL verification + 'http_errors' => false, // Don't throw exceptions for 4xx/5xx responses + 'allow_redirects' => true, // Follow redirects by default + 'headers' => [ + 'User-Agent' => 'FunAdmin/PHP-HTTP-Client', + 'X-REQUESTED-WITH' => 'XMLHttpRequest', + 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', + 'Accept-Encoding' => 'gzip, deflate', + 'Accept-Language' => 'zh-CN,en-US;q=0.8', + 'Referer' => dirname(request()->url()), + ] + ]; + + // Merge user options with defaults, letting user options override defaults + $options = array_merge($defaultOptions, $options); + // Merge additional headers if (!empty($header)) { $options['headers'] = array_merge($options['headers'], $header); } + // 正确处理Cookies - 使用GuzzleHttp的CookieJar if (!empty($cookies)) { + // 检查cookies是否已经是CookieJar对象 + if (!($cookies instanceof CookieJarInterface)) { + // 使用辅助方法转换为CookieJar对象 + $cookies = self::createCookieJar($cookies); + } + $options['cookies'] = $cookies; } return new Client($options); } + + /** + * 创建Guzzle Cookie容器 + * @param array $cookies Cookie数组 + * @param string $domain 可选,Cookie所属域名,默认从当前请求中获取 + * @return \GuzzleHttp\Cookie\CookieJarInterface + */ + public static function createCookieJar(array $cookies, $domain = null) + { + $cookieJar = new CookieJar(); + + if (empty($domain)) { + $domain = parse_url(request()->url(), PHP_URL_HOST); + } + + foreach ($cookies as $name => $value) { + if (is_array($value)) { + // 如果cookie是一个数组,包含详细设置 + $cookieConfig = array_merge([ + 'Domain' => $domain, + 'Path' => '/', + 'Secure' => false, + 'HttpOnly' => false + ], $value); + + // 确保Name和Value存在 + if (!isset($cookieConfig['Name']) && !is_numeric($name)) { + $cookieConfig['Name'] = $name; + } + + $cookieJar->setCookie(new SetCookie($cookieConfig)); + } else { + // 简单的键值形式 + $cookieJar->setCookie(new SetCookie([ + 'Name' => $name, + 'Value' => $value, + 'Domain' => $domain, + 'Path' => '/', + 'Secure' => false, + 'HttpOnly' => false + ])); + } + } + + return $cookieJar; + } } diff --git a/extend/fun/mcp/McpServer.php b/extend/fun/mcp/McpServer.php new file mode 100644 index 000000000..7180129dd --- /dev/null +++ b/extend/fun/mcp/McpServer.php @@ -0,0 +1,194 @@ +setName('mcp') + ->setDescription('启动MCP(Model Context Protocol)服务器') + ->addArgument('action', Argument::OPTIONAL, '执行的操作 (start|info)', 'start') + ->addOption('host', 'H', Option::VALUE_OPTIONAL, 'HTTP/SSE服务器监听地址', '127.0.0.1') + ->addOption('port', 'p', Option::VALUE_OPTIONAL, 'HTTP/SSE服务器监听端口', '8080') + ->addOption('transport', 't', Option::VALUE_OPTIONAL, '传输协议 (stdio|http|sse)', 'sse') + ->setHelp('此命令用于启动和管理MCP服务器'); + } + + /** + * 执行命令 + * @param Input $input + * @param Output $output + * @return int + */ + protected function execute(Input $input, Output $output) + { + $action = $input->getArgument('action'); + + switch ($action) { + case 'start': + return $this->startServer($input, $output); + + case 'info': + return $this->showInfo($input, $output); + + default: + $output->error("未知的操作: {$action}"); + return 1; + } + } + + /** + * 启动MCP服务器 + * @param Input $input + * @param Output $output + * @return int + */ + protected function startServer(Input $input, Output $output): int + { + try { + $transport = $input->getOption('transport'); + $mcpService = app(McpService::class); + + + $output->info('正在启动FunAdmin MCP服务器...'); + $output->info('服务器信息:'); + + $serviceInfo = $mcpService->getServiceInfo(); + $output->info(" 名称: {$serviceInfo['name']}"); + $output->info(" 版本: {$serviceInfo['version']}"); + $output->info(" 工具数量: {$serviceInfo['tools']}"); + $output->info(" 资源数量: {$serviceInfo['resources']}"); + $output->info(" 传输协议: {$transport}"); + if ($transport === 'sse') { + $host = $input->getOption('host'); + $port = $input->getOption('port'); + + $output->info('使用SSE传输协议启动服务器...'); + $output->info("监听地址: http://{$host}:{$port}/mcp"); + $output->info('服务器已准备就绪,等待客户端连接...'); + + // 启动SSE服务器(使用StreamableHttpServerTransport) + $mcpService->startWithSse($host, $port, 'mcp'); + + } elseif ($transport === 'stdio') { + if (PHP_OS_FAMILY === 'Windows') { + $output->error('在Windows系统上,STDIO传输不支持非阻塞管道'); + $output->info('建议使用SSE传输: php think mcp:server start --transport=sse'); + return 1; + } + $output->info('使用STDIO传输协议启动服务器...'); + $output->info('服务器已准备就绪,等待客户端连接...'); + // 启动STDIO服务器 + $mcpService->startWithStdio(); + + } elseif ($transport === 'http') { + $host = $input->getOption('host'); + $port = $input->getOption('port'); + + $output->info("使用HTTP传输协议启动服务器..."); + $output->info("监听地址: http://{$host}:{$port}/mcp"); + $output->info('服务器已准备就绪,等待客户端连接...'); + + // 启动HTTP服务器(使用HttpServerTransport) + $mcpService->startWithHttp($host, $port, 'mcp'); + + } else { + $output->error("不支持的传输协议: {$transport}"); + return 1; + } + + return 0; + + } catch (\Exception $e) { + $output->error('启动MCP服务器失败: ' . $e->getMessage()); + $output->error('错误详情: ' . $e->getTraceAsString()); + return 1; + } + } + + /** + * 显示服务器信息 + * @param Input $input + * @param Output $output + * @return int + */ + protected function showInfo(Input $input, Output $output): int + { + try { + $mcpService = McpService::instance(); + $serviceInfo = $mcpService->getServiceInfo(); + + $output->info('=== FunAdmin MCP服务器信息 ==='); + $output->info("服务名称: {$serviceInfo['name']}"); + $output->info("服务版本: {$serviceInfo['version']}"); + $output->info("工具数量: {$serviceInfo['tools']}"); + $output->info("资源数量: {$serviceInfo['resources']}"); + $output->info("服务状态: {$serviceInfo['status']}"); + + $output->info(''); + $output->info('=== 可用工具 ==='); + $tools = [ + 'db_query' => '执行数据库查询操作(仅支持SELECT语句)', + 'get_config' => '获取系统配置信息', + 'write_log' => '写入系统日志', + 'file_operation' => '文件读写操作', + 'user_management' => '用户管理相关操作', + 'system_info' => '获取系统运行信息' + ]; + + foreach ($tools as $name => $description) { + $output->info(" - {$name}: {$description}"); + } + + $output->info(''); + $output->info('=== 可用资源 ==='); + $resources = [ + 'config://system' => '系统配置信息', + 'schema://database' => '数据库表结构信息', + 'docs://api' => 'API接口文档' + ]; + + foreach ($resources as $uri => $description) { + $output->info(" - {$uri}: {$description}"); + } + + $output->info(''); + $output->info('=== 使用说明 ==='); + $output->info('启动STDIO服务器: php think mcp:server start'); + $output->info('启动HTTP服务器: php think mcp:server start --transport=http'); + $output->info('查看服务器信息: php think mcp:server info'); + + return 0; + + } catch (\Exception $e) { + $output->error('获取服务器信息失败: ' . $e->getMessage()); + return 1; + } + } +} diff --git a/mcp-server.php b/mcp-server.php new file mode 100644 index 000000000..ff8733e47 --- /dev/null +++ b/mcp-server.php @@ -0,0 +1,295 @@ +#!/usr/bin/env php +initialize(); + echo "✓ ThinkPHP应用初始化成功\n"; +} catch (\Throwable $e) { + echo "✗ ThinkPHP初始化失败: " . $e->getMessage() . "\n"; + exit(1); +} + +// 加载配置文件 +$configFile = __DIR__ . '/config/mcp.php'; +if (file_exists($configFile)) { + $mcpConfig = config('mcp'); +} else { + $mcpConfig = []; +} + +// 获取传输协议配置 +$transport = $mcpConfig['transport'] ?? 'sse'; +$host = $mcpConfig['host'] ?? '127.0.0.1'; +$port = (int)($mcpConfig['port'] ?? 8080); +$mcpPath = $mcpConfig['path'] ?? 'mcp'; + +// 设置超时和内存限制 +$timeout = $mcpConfig['timeout'] ?? 300000; +$memoryLimit = $mcpConfig['memory_limit'] ?? '512M'; +$maxExecutionTime = $mcpConfig['max_execution_time'] ?? 0; + +// 应用配置 +ini_set('memory_limit', $memoryLimit); +ini_set('max_execution_time', $maxExecutionTime); +set_time_limit(0); +ignore_user_abort(true); + +// 设置错误处理 +ini_set('log_errors', '1'); +ini_set('error_log', __DIR__ . '/runtime/log/mcp_error.log'); + +echo "=== FunAdmin MCP服务器启动(稳定版)===\n"; +echo "传输协议: {$transport}\n"; +echo "监听地址: {$host}:{$port}\n"; +echo "超时设置: {$timeout}ms\n"; +echo "内存限制: {$memoryLimit}\n"; +echo "执行时间限制: {$maxExecutionTime}s\n"; + +use app\common\service\McpService; +use think\facade\Log; + +/** + * 标准错误输出日志记录器 + */ +class StderrLogger extends \Psr\Log\AbstractLogger +{ + public function log($level, \Stringable|string $message, array $context = []): void + { + $contextStr = empty($context) ? '' : ' ' . json_encode($context, JSON_UNESCAPED_UNICODE); + fwrite(STDERR, sprintf( + "[%s] [%s] %s%s\n", + date('Y-m-d H:i:s'), + strtoupper($level), + $message, + $contextStr + )); + } +} + +/** + * 信号处理函数 + */ +function handleSignal($signal) { + $logger = new StderrLogger(); + $logger->info("接收到信号: {$signal},正在优雅关闭服务器..."); + + // 清理资源 + if (function_exists('gc_collect_cycles')) { + gc_collect_cycles(); + } + + exit(0); +} + +// 注册信号处理器 +if (function_exists('pcntl_signal')) { + pcntl_signal(SIGTERM, 'handleSignal'); + pcntl_signal(SIGINT, 'handleSignal'); + pcntl_signal(SIGHUP, 'handleSignal'); +} + +/** + * 连接监控函数 + */ +function monitorConnection($mcpService, $logger) { + $lastHeartbeat = time(); + $heartbeatInterval = 30; // 30秒心跳间隔 + + while (true) { + try { + $currentTime = time(); + + // 检查心跳 + if ($currentTime - $lastHeartbeat >= $heartbeatInterval) { + $logger->debug('发送心跳信号'); + $lastHeartbeat = $currentTime; + + // 检查内存使用 + $memoryUsage = memory_get_usage(true); + $memoryLimit = ini_get('memory_limit'); + $logger->debug('内存使用情况', [ + 'usage' => formatBytes($memoryUsage), + 'limit' => $memoryLimit + ]); + } + + // 检查连接状态 + $serviceInfo = $mcpService->getServiceInfo(); + if ($serviceInfo['status'] !== 'ready') { + $logger->warning('服务状态异常', $serviceInfo); + } + + sleep(5); // 每5秒检查一次 + + } catch (\Throwable $e) { + $logger->error('连接监控错误: ' . $e->getMessage()); + sleep(10); // 错误后等待10秒 + } + } +} + +/** + * 格式化字节数 + */ +function formatBytes($bytes) { + $units = ['B', 'KB', 'MB', 'GB']; + $bytes = max($bytes, 0); + $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); + $pow = min($pow, count($units) - 1); + $bytes /= pow(1024, $pow); + return round($bytes, 2) . ' ' . $units[$pow]; +} + +try { + $logger = new StderrLogger(); + $logger->info("正在启动FunAdmin MCP服务器 (传输协议: {$transport})..."); + + echo "创建MCP服务实例...\n"; + + // 创建MCP服务实例并设置日志记录器 + $mcpService = app(McpService::class); + $mcpService->setLogger($logger); + + echo "✓ MCP服务实例创建成功\n"; + + $serviceInfo = $mcpService->getServiceInfo(); + + $logger->info('服务器配置信息:', [ + 'name' => $serviceInfo['name'], + 'version' => $serviceInfo['version'], + 'tools' => $serviceInfo['tools'], + 'resources' => $serviceInfo['resources'], + 'transport' => $transport, + 'timeout' => $timeout, + 'memory_limit' => $memoryLimit, + 'config' => $serviceInfo['config'] ?? [] + ]); + + // 启动连接监控(在后台线程中) + if (function_exists('pcntl_fork')) { + $monitorPid = pcntl_fork(); + if ($monitorPid == 0) { + // 子进程执行监控 + monitorConnection($mcpService, $logger); + exit(0); + } + } + + // 根据传输协议启动服务器 + switch ($transport) { + case 'stdio': + if (PHP_OS_FAMILY === 'Windows') { + echo "✗ 在Windows系统上,STDIO传输不支持非阻塞管道\n"; + echo "建议使用SSE传输: MCP_TRANSPORT=sse php mcp-server-stable.php\n"; + exit(1); + } + + echo "使用STDIO传输协议启动服务器...\n"; + echo "服务器已准备就绪,等待客户端连接...\n"; + $mcpService->startWithStdio(); + break; + + case 'http': + echo "使用HTTP传输协议启动服务器...\n"; + echo "监听地址: http://{$host}:{$port}/{$mcpPath}\n"; + echo "服务器已准备就绪,等待HTTP连接...\n"; + $mcpService->startWithHttp($host, $port, $mcpPath); + break; + + case 'sse': + echo "使用SSE传输协议启动服务器...\n"; + echo "监听地址: http://{$host}:{$port}/{$mcpPath}\n"; + echo "服务器已准备就绪,等待SSE连接...\n"; + $mcpService->startWithSse($host, $port, $mcpPath); + break; + + default: + echo "✗ 不支持的传输协议: {$transport}\n"; + echo "支持的协议: stdio, http, sse\n"; + echo "使用示例:\n"; + echo " MCP_TRANSPORT=stdio php mcp-server-stable.php\n"; + echo " MCP_TRANSPORT=http php mcp-server-stable.php\n"; + echo " MCP_TRANSPORT=sse php mcp-server-stable.php\n"; + exit(1); + } + + $logger->info('MCP服务器已正常关闭'); + exit(0); + +} catch (\Throwable $e) { + $logger = new StderrLogger(); + $logger->critical('MCP服务器启动失败', [ + 'error' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'trace' => explode("\n", $e->getTraceAsString()) + ]); + + echo "\n=== 详细错误信息 ===\n"; + echo "错误: " . $e->getMessage() . "\n"; + echo "文件: " . $e->getFile() . ":" . $e->getLine() . "\n"; + echo "堆栈跟踪:\n" . $e->getTraceAsString() . "\n"; + + // 记录到ThinkPHP日志 + if (class_exists('\think\facade\Log')) { + Log::critical('MCP服务器启动失败', [ + 'error' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine() + ]); + } + + exit(1); +} + +#mcp 配置 +// { +// "mcpServers": { +// "funadmin": { +// "url":"127.0.0.1:8081/mcp" +// } +// } +// } diff --git a/mcp-start.bat b/mcp-start.bat new file mode 100644 index 000000000..ca809b99b --- /dev/null +++ b/mcp-start.bat @@ -0,0 +1,25 @@ +@echo off +echo ======================================== +echo FunAdmin MCP 服务器启动脚本 +echo ======================================== + +REM 设置环境变量 +set MCP_TRANSPORT=sse +set MCP_HOST=127.0.0.1 +set MCP_PORT=8080 +set MCP_PATH=mcp +set MCP_TIMEOUT=60000 +set MCP_MEMORY_LIMIT=256M +set MCP_MAX_EXECUTION_TIME=0 + +echo 传输协议: %MCP_TRANSPORT% +echo 监听地址: %MCP_HOST%:%MCP_PORT% +echo 超时设置: %MCP_TIMEOUT%ms +echo 内存限制: %MCP_MEMORY_LIMIT% +echo 执行时间限制: %MCP_MAX_EXECUTION_TIME%s + +echo. +echo 正在启动 MCP 服务器... +php mcp-server.php + +pause diff --git a/mcp-start.sh b/mcp-start.sh new file mode 100644 index 000000000..2692b68d1 --- /dev/null +++ b/mcp-start.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +echo "========================================" +echo "FunAdmin MCP 服务器启动脚本" +echo "========================================" + +# 设置环境变量 +export MCP_TRANSPORT=sse +export MCP_HOST=127.0.0.1 +export MCP_PORT=8080 +export MCP_PATH=mcp +export MCP_TIMEOUT=60000 +export MCP_MEMORY_LIMIT=256M +export MCP_MAX_EXECUTION_TIME=0 + +echo "传输协议: $MCP_TRANSPORT" +echo "监听地址: $MCP_HOST:$MCP_PORT" +echo "超时设置: ${MCP_TIMEOUT}ms" +echo "内存限制: $MCP_MEMORY_LIMIT" +echo "执行时间限制: ${MCP_MAX_EXECUTION_TIME}s" + +echo "" +echo "正在启动 MCP 服务器..." +php mcp-server.php diff --git a/public/.gitignore b/public/.gitignore index ed790048f..763aa6793 100644 --- a/public/.gitignore +++ b/public/.gitignore @@ -1,3 +1,6 @@ install.lock xxx.php -./storage \ No newline at end of file +./storage +/.idea +/.vscode +/.DS_Store \ No newline at end of file diff --git a/public/static/.DS_Store b/public/static/.DS_Store new file mode 100644 index 000000000..ee287b6e2 Binary files /dev/null and b/public/static/.DS_Store differ diff --git a/public/static/backend/css/comm.css b/public/static/backend/css/comm.css index 656c4c05e..25bc78a7a 100644 --- a/public/static/backend/css/comm.css +++ b/public/static/backend/css/comm.css @@ -1,503 +1,431 @@ -@import url("../../common/css/iconfont.css");::-webkit-input-placeholder{color:#ccc;font-size:12px} -::-webkit-scrollbar{width:8px !important;height:10px !important;background:#ffffff !important;cursor:pointer !important;overflow-y:auto} -::-webkit-scrollbar-thumb{background-color:#9C9DA098;-webkit-border-radius:2em;-moz-border-radius:2em;border-radius:2em} -::-webkit-scrollbar-track{-webkit-box-shadow:inset 0 0 5px rgba(240,240,240,.5) !important;border-radius:0 !important;background:rgba(240,240,240,0.5) !important;cursor:pointer !important} -html{-ms-overflow-style:none;overflow:-moz-scrollbars-none} -body{background:#f1f4f6;padding:10px} -/*子页面主题*/ -body .layui-form-onswitch{border:none;} -/*去除border*/ -.layui-form-checked[lay-skin=primary] i{border:none;} -.fun-container{background:#fff} -.layui-table,.layui-table-view{margin:0 auto} -.fl{float:left}.fr{float:right}.clearfix{clear:both} -.layui-table-tool{z-index:80} -.layui-table-tool-temp{padding-right:60px} -.layui-icon{} -.layui-table-tool-temp{padding-right:120px} -.layui-form-item .layui-form-mid.layui-word-aux{float: none;} -.layui-form-item .required::after{content:"*";color:red;position:absolute;margin-left:4px;font-weight:bold;line-height:1.8em;top:6px;right:5px} -.layui-input:focus, .layui-textarea:focus{border-color:#4d70ff!important; } -.layui-text-center,.layui-btn-center{text-align:center} -.layui-text-left{text-align:left} -.layui-text-right{text-align:right} -.layui-upload-list li{display:inline-block} -.layui-upload-list li i{background:#f2f2f2;cursor:pointer} -.layui-search-fieldset form{padding:10px} -.layui-search-fieldset .layui-btn{height:32px;line-height:28px;font-size:12px;padding:0 10px} -.layui-search-fieldset .layui-form-label{padding:0 8px;height:32px;line-height:30px;text-align: left;} -.layui-search-fieldset .layui-input-inline input,.layui-search-fieldset .layui-input-inline select{width:100%;height:32px;padding:2px 8px;line-height:1em} -.layui-search-fieldset .layui-between .layui-input-inline{margin-right:3%;width:30%} -.layui-search-fieldset .layui-between .layui-input-inline + .layui-input-inline:before{content:"-";position:absolute;left:-6%;top:6px} -.layui-search-fieldset .layui-form-select dl{top:31px;padding:0} -.layui-form-select dl dd.layui-this{background:#4d70ff;color:#fff} -.layui-form-checked span, .layui-form-checked:hover span{background-color:#4d70ff} - -.fun-layer-class .layui-layer-btn a:last-child{background:#fff;color:#333;border:1px solid #dedede;} -.fun-layer-class .layui-layer-title{background:rgb(243, 246, 246);border-bottom: none;} -.fun-layer-class .layui-layer-btn.layui-layer-btn-c{background-color:rgb(243, 246, 246)} -.layui-form-item.layui-btn-center button.submit{background: linear-gradient(0deg, rgba(0,172,238,1) 0%, rgba(2,126,251,1) 100%);color:#fff;border:none} -.layui-form-item.layui-btn-center button.reset{background:#eee} -.layui-form-item.layui-btn-center button:hover{border-color:#eee;} - - -/* 偶数行背景色 */ -.layui-table tr:nth-child(even) {background-color: rgb(243 245 243);} -/* 鼠标指向表格时,奇数行背景颜色 */ -.layui-table tbody tr:hover,.layui-table-hover {background-color: #eeeeee;} -.layui-form-item input[lay-filter=date] {padding-left:36px;} -.layui-form-item .layui-icon.layui-icon-date {color: #f44336;} -.layui-form-radio:hover *, .layui-form-radioed, .layui-form-radioed>i{color: #4d70ff} -.layui-form-checked[lay-skin=primary] i{border-color: #4d70ff!important;background-color: #4d70ff;} -/*!* 设置下拉框的高度与表格单元格的高度相同 *!*/ - - -#theme0 .layui-btn, -#theme0 .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme0 .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme0 .layui-form-onswitch, -#theme0 .fun-layer-class .layui-layer-title {background:#4d70ff!important;color: #fff;border:none;} #theme0 blockquote{border-left-color:#4d70ff ;} -#theme0 .layui-input:focus, #theme0 .layui-textarea:focus {border-color: #4d70ff!important;} -#theme0 .layui-laydate-main .layui-laydate-header{background: #4d70ff!important;} -#theme0 .layui-laydate-main .layui-this{background: #4d70ff!important;} -#theme0 .layui-laydate-footer span:hover{color: #4d70ff!important;} -#theme0 .layui-form-radio>i:hover, #theme0 .layui-form-radioed>i, #theme0 .layui-form-checked i, #theme0 .layui-form-checked:hover i {color: #4d70ff;} -#theme0 .layui-form-select .layui-form-checked span,#theme0 .layui-form-select .layui-form-checked:hover span{background: #4d70ff!important;} -#theme0 .fun-layer-class .layui-layer-title {background:#4d70ff!important;color: #fff;} -#theme0 .layui-form-select dl dd.layui-this{ background: #4d70ff!important;} -#theme0 .multi dl dd.layui-this{ background: none!important;} -#theme0 .multi .layui-form-checked span, #theme0 .multi .layui-form-checked:hover span{background: none!important;} -#theme0 .multi .layui-form-checked[lay-skin=primary] i{background: #4d70ff!important;} -#theme0 .multi .layui-form-radio>i:hover, #theme0 .multi .layui-form-radioed>i, #theme0 .multi .layui-form-checked i, -#theme0 .multi .layui-form-checked:hover i{color: #fff} -#theme0 .multi .layui-form-checked span,#theme0 .multi .layui-form-checked:hover span{background: #fff!important;} -#theme0 .layui-form-radio:hover *,#theme0 .layui-form-radioed,#theme0 .layui-form-radioed>i{color: #4d70ff} - -#theme1 .layui-btn, -#theme1 .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme1 .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme1 .layui-form-onswitch, -#theme1 .fun-layer-class .layui-layer-title {background:#20b598!important;color: #fff;border:none;} #theme1 blockquote{border-left-color:#20b598 ;} -#theme1 .layui-input:focus, #theme1 .layui-textarea:focus {border-color: #20b598!important;} -#theme2 .layui-laydate-main .layui-laydate-header{background: #20b598!important;} -#theme2 .layui-laydate-main .layui-this{background: #20b598!important;} -#theme2 .layui-laydate-footer span:hover{color: #20b598!important;} -#theme1 .layui-form-radio>i:hover, #theme1 .layui-form-radioed>i, #theme1 .layui-form-checked i, #theme1 .layui-form-checked:hover i {color: #20b598;} -#theme1 .layui-form-checked span, #theme1 .layui-form-checked:hover span{background: #20b598!important;} -#theme1 .fun-layer-class .layui-layer-title {background:#20b598!important;color: #fff;} -#theme1 .layui-form-select dl dd.layui-this{ background: #20b598!important;} -#theme1 .multi dl dd.layui-this{ background: none!important;} -#theme1 .multi .layui-form-checked span, #theme1 .multi .layui-form-checked:hover span{background: none!important;} -#theme1 .multi .layui-form-checked[lay-skin=primary] i{background: #20b598!important;} -#theme1 .multi .layui-form-radio>i:hover, #theme1 .multi .layui-form-radioed>i, #theme1 .multi .layui-form-checked i, -#theme1 .multi .layui-form-checked:hover i{color: #fff} -#theme1 .multi .layui-form-checked span,#theme1 .multi .layui-form-checked:hover span{background: #fff!important;} -#theme1 .layui-form-radio:hover *,#theme1 .layui-form-radioed,#theme1 .layui-form-radioed>i{color: #20b598} - - -#theme2 .layui-btn, -#theme2 .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme2 .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme2 .layui-form-onswitch, -#theme2 .fun-layer-class .layui-layer-title {background:#8e10b9!important;color: #fff;border:none;} #theme2 blockquote{border-left-color:#8e10b9 ;} -#theme2 .layui-input:focus, #theme2 .layui-textarea:focus {border-color: #8e10b9!important;} -#theme2 .layui-laydate-main .layui-laydate-header{background: #8e10b9!important;} -#theme2 .layui-laydate-main .layui-this{background: #8e10b9!important;} -#theme2 .layui-laydate-footer span:hover{color: #8e10b9!important;} -#theme2 .layui-form-radio>i:hover, #theme2 .layui-form-radioed>i, #theme2 .layui-form-checked i, #theme2 .layui-form-checked:hover i {color: #8e10b9;} -#theme2 .layui-form-checked span, #theme2 .layui-form-checked:hover span{background: #8e10b9!important;} -#theme2 .fun-layer-class .layui-layer-title {background:#8e10b9!important;color: #fff;} -#theme2 .layui-form-select dl dd.layui-this{ background: #8e10b9!important;} -#theme2 .multi dl dd.layui-this{ background: none!important;} -#theme2 .multi .layui-form-checked span, #theme2 .multi .layui-form-checked:hover span{background: none!important;} -#theme2 .multi .layui-form-checked[lay-skin=primary] i{background: #8e10b9!important;} -#theme2 .multi .layui-form-radio>i:hover, #theme2 .multi .layui-form-radioed>i, #theme2 .multi .layui-form-checked i, -#theme2 .multi .layui-form-checked:hover i{color: #fff} -#theme2 .multi .layui-form-checked span,#theme2 .multi .layui-form-checked:hover span{background: #fff!important;} -#theme2 .layui-form-radio:hover *,#theme2 .layui-form-radioed,#theme2 .layui-form-radioed>i{color: #8e10b9} - -#theme3 .layui-btn, -#theme3 .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme3 .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme3 .layui-form-onswitch, -#theme3 .fun-layer-class .layui-layer-title {background:#b8279f!important;color: #fff;border:none;} #theme3 blockquote{border-left-color:#b8279f ;} -#theme3 .layui-input:focus, #theme3 .layui-textarea:focus {border-color: #b8279f!important;} -#theme3 .layui-laydate-main .layui-laydate-header{background: #b8279f!important;} -#theme3 .layui-laydate-main .layui-this{background: #b8279f!important;} -#theme3 .layui-laydate-footer span:hover{color: #b8279f!important;} -#theme3 .layui-form-radio>i:hover, #theme3 .layui-form-radioed>i, #theme3 .layui-form-checked i, #theme3 .layui-form-checked:hover i {color: #b8279f;} -#theme3 .layui-form-checked span,#theme3 .layui-form-checked:hover span{background: #b8279f!important;} -#theme3 .fun-layer-class .layui-layer-title {background:#b8279f!important;color: #fff;} -#theme3 .layui-form-select dl dd.layui-this{ background: #b8279f!important;} -#theme3 .multi dl dd.layui-this{ background: none!important;} -#theme3 .multi .layui-form-checked span, #theme3 .multi .layui-form-checked:hover span{background: none!important;} -#theme3 .multi .layui-form-checked[lay-skin=primary] i{background: #b8279f!important;} -#theme3 .multi .layui-form-radio>i:hover, #theme3 .multi .layui-form-radioed>i, #theme3 .multi .layui-form-checked i, -#theme3 .multi .layui-form-checked:hover i{color: #fff} -#theme3 .multi .layui-form-checked span,#theme3 .multi .layui-form-checked:hover span{background: #fff!important;} -#theme3 .layui-form-radio:hover *,#theme3 .layui-form-radioed,#theme3 .layui-form-radioed>i{color: #b8279f} - -#theme4 .layui-btn, -#theme4 .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme4 .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme4 .layui-form-onswitch, -#theme4 .fun-layer-class .layui-layer-title {background:#20b598!important;color: #fff;border:none;} #theme4 blockquote{border-left-color:#20b598 ;} -#theme4 .layui-input:focus, #theme4 .layui-textarea:focus {border-color: #20b598!important;} -#theme4 .layui-laydate-main .layui-laydate-header{background: #20b598!important;} -#theme4 .layui-laydate-main .layui-this{background: #20b598!important;} -#theme4 .layui-laydate-footer span:hover{color: #20b598!important;} -#theme4 .layui-form-radio>i:hover, #theme4 .layui-form-radioed>i, #theme4 .layui-form-checked i, #theme4 .layui-form-checked:hover i {color: #20b598;} -#theme4 .layui-form-checked span,#theme4 .layui-form-checked:hover span{background: #20b598!important;} -#theme4 .fun-layer-class .layui-layer-title {background:#20b598!important;color: #fff;} -#theme4 .layui-form-select dl dd.layui-this{ background: #20b598!important;} -#theme4 .multi dl dd.layui-this{ background: none!important;} -#theme4 .multi .layui-form-checked span, #theme4 .multi .layui-form-checked:hover span{background: none!important;} -#theme4 .multi .layui-form-checked[lay-skin=primary] i{background: #20b598!important;} -#theme4 .multi .layui-form-radio>i:hover, #theme4 .multi .layui-form-radioed>i, #theme4 .multi .layui-form-checked i, -#theme4 .multi .layui-form-checked:hover i{color: #fff} -#theme4 .multi .layui-form-checked span,#theme4 .multi .layui-form-checked:hover span{background: #fff!important;} -#theme4 .layui-form-radio:hover *,#theme4 .layui-form-radioed,#theme4 .layui-form-radioed>i{color: #20b598} - -#theme5 .layui-btn, -#theme5 .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme5 .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme5 .layui-form-onswitch, -#theme5 .fun-layer-class .layui-layer-title {background:#8e10b9!important;color: #fff;border:none;} #theme5 blockquote{border-left-color:#8e10b9 ;} -#theme5 .layui-input:focus, #theme5 .layui-textarea:focus {border-color: #8e10b9!important;} -#theme5 .layui-laydate-main .layui-laydate-header{background: #8e10b9!important;} -#theme5 .layui-laydate-main .layui-this{background: #8e10b9!important;} -#theme5 .layui-laydate-footer span:hover{color: #8e10b9!important;} -#theme5 .layui-form-radio>i:hover, #theme5 .layui-form-radioed>i, #theme5 .layui-form-checked i, #theme5 .layui-form-checked:hover i {color: #8e10b9;} -#theme5 .layui-form-checked span,#theme5 .layui-form-checked:hover span{background: #8e10b9!important;} -#theme5 .fun-layer-class .layui-layer-title {background:#8e10b9!important;color: #fff;} -#theme5 .layui-form-select dl dd.layui-this{ background: #8e10b9!important;} -#theme5 .multi dl dd.layui-this{ background: none!important;} -#theme5 .multi .layui-form-checked span, #theme5 .multi .layui-form-checked:hover span{background: none!important;} -#theme5 .multi .layui-form-checked[lay-skin=primary] i{background: #8e10b9!important;} -#theme5 .multi .layui-form-radio>i:hover, #theme5 .multi .layui-form-radioed>i, #theme5 .multi .layui-form-checked i, -#theme5 .multi .layui-form-checked:hover i{color: #fff} -#theme5 .multi .layui-form-checked span,#theme5 .multi .layui-form-checked:hover span{background: #fff!important;} -#theme5 .layui-form-radio:hover *,#theme5 .layui-form-radioed,#theme5 .layui-form-radioed>i{color: #8e10b9} - - -#theme6 .layui-btn, -#theme6 .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme6 .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme6 .layui-form-onswitch, -#theme6 .fun-layer-class .layui-layer-title {background:#20b598!important;color: #fff;border:none;} #theme6 blockquote{border-left-color:#20b598 ;} -#theme6 .layui-input:focus, #theme6 .layui-textarea:focus {border-color: #20b598!important;} -#theme6 .layui-laydate-main .layui-laydate-header{background: #20b598!important;} -#theme6 .layui-laydate-main .layui-this{background: #20b598!important;} -#theme6 .layui-laydate-footer span:hover{color: #20b598!important;} -#theme6 .layui-form-radio>i:hover, #theme6 .layui-form-radioed>i, #theme6 .layui-form-checked i, #theme6 .layui-form-checked:hover i {color: #20b598;} -#theme6 .layui-form-checked span,#theme6 .layui-form-checked:hover span{background: #20b598!important;} -#theme6 .fun-layer-class .layui-layer-title {background:#20b598!important;color: #fff;} -#theme6 .layui-form-select dl dd.layui-this{ background: #20b598!important;} -#theme6 .multi dl dd.layui-this{ background: none!important;} -#theme6 .multi .layui-form-checked span, #theme6 .multi .layui-form-checked:hover span{background: none!important;} -#theme6 .multi .layui-form-checked[lay-skin=primary] i{background: #20b598!important;} -#theme6 .multi .layui-form-radio>i:hover, #theme6 .multi .layui-form-radioed>i, #theme6 .multi .layui-form-checked i, -#theme6 .multi .layui-form-checked:hover i{color: #fff} -#theme6 .multi .layui-form-checked span,#theme6 .multi .layui-form-checked:hover span{background: #fff!important;} -#theme6 .layui-form-radio:hover *,#theme6 .layui-form-radioed,#theme6 .layui-form-radioed>i{color: #20b598} - -#theme7 .layui-btn, -#theme7 .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme7 .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme7 .layui-form-onswitch, -#theme7 .fun-layer-class .layui-layer-title {background:#20b598!important;color: #fff;border:none;} #theme7 blockquote{border-left-color:#20b598 ;} -#theme7 .layui-input:focus, #theme7 .layui-textarea:focus {border-color: #20b598!important;} -#theme7 .layui-laydate-main .layui-laydate-header{background: #20b598!important;} -#theme7 .layui-laydate-main .layui-this{background: #20b598!important;} -#theme7 .layui-laydate-footer span:hover{color: #20b598!important;} -#theme7 .layui-form-radio>i:hover, #theme7 .layui-form-radioed>i, #theme7 .layui-form-checked i, #theme7 .layui-form-checked:hover i {color: #20b598;} -#theme7 .layui-form-checked span,#theme7 .layui-form-checked:hover span{background: #20b598!important;} -#theme7 .fun-layer-class .layui-layer-title {background:#20b598!important;color: #fff;} -#theme7 .layui-form-select dl dd.layui-this{ background: #20b598!important;} -#theme7 .multi dl dd.layui-this{ background: none!important;} -#theme7 .multi .layui-form-checked span, #theme7 .multi .layui-form-checked:hover span{background: none!important;} -#theme7 .multi .layui-form-checked[lay-skin=primary] i{background: #20b598!important;} -#theme7 .multi .layui-form-radio>i:hover, #theme7 .multi .layui-form-radioed>i, #theme7 .multi .layui-form-checked i, -#theme7 .multi .layui-form-checked:hover i{color: #fff} -#theme7 .multi .layui-form-checked span,#theme7 .multi .layui-form-checked:hover span{background: #fff!important;} -#theme7 .layui-form-radio:hover *,#theme7 .layui-form-radioed,#theme7 .layui-form-radioed>i{color: #20b598} - -#theme8 .layui-btn, -#theme8 .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme8 .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme8 .layui-form-onswitch, -#theme8 .fun-layer-class .layui-layer-title {background:#00a65a!important;color: #fff;border:none;} #theme8 blockquote{border-left-color:#00a65a ;} -#theme8 .layui-input:focus, #theme8 .layui-textarea:focus {border-color: #00a65a!important;} -#theme8 .layui-laydate-main .layui-laydate-header{background: #00a65a!important;} -#theme8 .layui-laydate-main .layui-this{background: #00a65a!important;} -#theme8 .layui-laydate-footer span:hover{color: #00a65a!important;} -#theme8 .layui-form-radio>i:hover, #theme8 .layui-form-radioed>i, #theme8 .layui-form-checked i, #theme8 .layui-form-checked:hover i {color: #00a65a;} -#theme8 .layui-form-checked span,#theme8 .layui-form-checked:hover span{background: #00a65a!important;} -#theme8 .fun-layer-class .layui-layer-title {background:#00a65a!important;color: #fff;} -#theme8 .layui-form-select dl dd.layui-this{ background: #00a65a!important;} -#theme8 .multi dl dd.layui-this{ background: none!important;} -#theme8 .multi .layui-form-checked span, #theme8 .multi .layui-form-checked:hover span{background: none!important;} -#theme8 .multi .layui-form-checked[lay-skin=primary] i{background: #00a65a!important;} -#theme8 .multi .layui-form-radio>i:hover, #theme8 .multi .layui-form-radioed>i, #theme8 .multi .layui-form-checked i, -#theme8 .multi .layui-form-checked:hover i{color: #fff} -#theme8 .multi .layui-form-checked span,#theme8 .multi .layui-form-checked:hover span{background: #fff!important;} -#theme8 .layui-form-radio:hover *,#theme8 .layui-form-radioed,#theme8 .layui-form-radioed>i{color: #00a65a} - -#theme9 .layui-btn, -#theme9 .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme9 .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme9 .layui-form-onswitch, -#theme9 .fun-layer-class .layui-layer-title {background:#8e10b9!important;color: #fff;border:none;} #theme9 blockquote{border-left-color:#8e10b9 ;} -#theme9 .layui-input:focus, #theme9 .layui-textarea:focus {border-color: #8e10b9!important;} -#theme9 .layui-laydate-main .layui-laydate-header{background: #8e10b9!important;} -#theme9 .layui-laydate-main .layui-this{background: #8e10b9!important;} -#theme9 .layui-laydate-footer span:hover{color: #8e10b9!important;} -#theme9 .layui-form-radio>i:hover, #theme9 .layui-form-radioed>i, #theme9 .layui-form-checked i, #theme9 .layui-form-checked:hover i {color: #8e10b9;} -#theme9 .layui-form-checked span,#theme9 .layui-form-checked:hover span{background: #8e10b9!important;} -#theme9 .fun-layer-class .layui-layer-title {background:#8e10b9!important;color: #fff;} -#theme9 .layui-form-select dl dd.layui-this{ background: #8e10b9!important;} -#theme9 .multi dl dd.layui-this{ background: none!important;} -#theme9 .multi .layui-form-checked span, #theme9 .multi .layui-form-checked:hover span{background: none!important;} -#theme9 .multi .layui-form-checked[lay-skin=primary] i{background: #8e10b9!important;} -#theme9 .multi .layui-form-radio>i:hover, #theme9 .multi .layui-form-radioed>i, #theme9 .multi .layui-form-checked i, -#theme9 .multi .layui-form-checked:hover i{color: #fff} -#theme9 .multi .layui-form-checked span,#theme9 .multi .layui-form-checked:hover span{background: #fff!important;} -#theme9 .layui-form-radio:hover *,#theme9 .layui-form-radioed,#theme9 .layui-form-radioed>i{color: #8e10b9} - -#theme10 .layui-btn, -#theme10 .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme10 .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme10 .layui-form-onswitch, -#theme10 .fun-layer-class .layui-layer-title {background:#00a65a!important;color: #fff;border:none;} #theme10 blockquote{border-left-color:#00a65a ;} -#theme10 .layui-input:focus, #theme10 .layui-textarea:focus {border-color: #00a65a!important;} -#theme10 .layui-laydate-main .layui-laydate-header{background: #00a65a!important;} -#theme10 .layui-laydate-main .layui-this{background: #00a65a!important;} -#theme10 .layui-laydate-footer span:hover{color: #00a65a!important;} -#theme10 .layui-form-radio>i:hover, #theme10 .layui-form-radioed>i, #theme10 .layui-form-checked i, #theme10 .layui-form-checked:hover i {color: #00a65a;} -#theme10 .layui-form-checked span,#theme10 .layui-form-checked:hover span{background: #00a65a!important;} -#theme10 .fun-layer-class .layui-layer-title {background:#00a65a!important;color: #fff;} -#theme10 .layui-form-select dl dd.layui-this{ background: #20b598!important;} -#theme10 .multi dl dd.layui-this{ background: none!important;} -#theme10 .multi .layui-form-checked span, #theme10 .multi .layui-form-checked:hover span{background: none!important;} -#theme10 .multi .layui-form-checked[lay-skin=primary] i{background: #20b598!important;} -#theme10 .multi .layui-form-radio>i:hover, #theme10 .multi .layui-form-radioed>i, #theme10 .multi .layui-form-checked i, -#theme10 .multi .layui-form-checked:hover i{color: #fff} -#theme10 .multi .layui-form-checked span,#theme10 .multi .layui-form-checked:hover span{background: #fff!important;} -#theme10 .layui-form-radio:hover *,#theme10 .layui-form-radioed,#theme10 .layui-form-radioed>i{color: #20b598} - - -#theme11 .layui-btn, -#theme11 .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme11 .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme11 .layui-form-onswitch, -#theme11 .fun-layer-class .layui-layer-title {background:#ffa4d1!important;color: #fff;border:none;} #theme11 blockquote{border-left-color:#ffa4d1 ;} -#theme11 .layui-input:focus, #theme11 .layui-textarea:focus {border-color: #ffa4d1!important;} -#theme11 .layui-laydate-main .layui-laydate-header{background: #ffa4d1!important;} -#theme11 .layui-laydate-main .layui-this{background: #ffa4d1!important;} -#theme11 .layui-laydate-footer span:hover{color: #ffa4d1!important;} -#theme11 .layui-form-radio>i:hover, #theme11 .layui-form-radioed>i, #theme11 .layui-form-checked i, #theme11 .layui-form-checked:hover i {color: #ffa4d1;} -#theme11 .layui-form-checked span,#theme11 .layui-form-checked:hover span{background: #ffa4d1!important;} -#theme11 .fun-layer-class .layui-layer-title {background:#ffa4d1!important;color: #fff;} -#theme11 .layui-form-select dl dd.layui-this{ background: #ffa4d1!important;} -#theme11 .multi dl dd.layui-this{ background: none!important;} -#theme11 .multi .layui-form-checked span, #theme11 .multi .layui-form-checked:hover span{background: none!important;} -#theme11 .multi .layui-form-checked[lay-skin=primary] i{background: #ffa4d1!important;} -#theme11 .multi .layui-form-radio>i:hover, #theme11 .multi .layui-form-radioed>i, #theme11 .multi .layui-form-checked i, -#theme11 .multi .layui-form-checked:hover i{color: #fff} -#theme11 .multi .layui-form-checked span,#theme11 .multi .layui-form-checked:hover span{background: #fff!important;} -#theme11 .layui-form-radio:hover *,#theme11 .layui-form-radioed,#theme11 .layui-form-radioed>i{color: #ffa4d1} - -#theme12 .layui-btn, -#theme12 .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme12 .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme12 .layui-form-onswitch, -#theme12 .fun-layer-class .layui-layer-title {background:#1e9fff!important;color: #fff;border:none;} #theme12 blockquote{border-left-color:#1e9fff ;} -#theme12 .layui-input:focus, #theme12 .layui-textarea:focus {border-color: #1e9fff!important;} -#theme12 .layui-laydate-main .layui-laydate-header{background: #1e9fff!important;} -#theme12 .layui-laydate-main .layui-this{background: #1e9fff!important;} -#theme12 .layui-laydate-footer span:hover{color: #1e9fff!important;} -#theme12 .layui-form-radio>i:hover, #theme12 .layui-form-radioed>i, #theme12 .layui-form-checked i, #theme12 .layui-form-checked:hover i {color: #1e9fff;} -#theme12.layui-form-checked span,#theme12 .layui-form-checked:hover span{background: #1e9fff!important;} -#theme12 .fun-layer-class .layui-layer-title {background:#1e9fff!important;color: #fff;} -#theme12 .layui-form-select dl dd.layui-this{ background: #1e9fff!important;} -#theme12 .multi dl dd.layui-this{ background: none!important;} -#theme12 .multi .layui-form-checked span, #theme12 .multi .layui-form-checked:hover span{background: none!important;} -#theme12 .multi .layui-form-checked[lay-skin=primary] i{background: #1e9fff!important;} -#theme12 .multi .layui-form-radio>i:hover, #theme12 .multi .layui-form-radioed>i, #theme12 .multi .layui-form-checked i, -#theme12 .multi .layui-form-checked:hover i{color: #fff} -#theme12 .multi .layui-form-checked span,#theme12 .multi .layui-form-checked:hover span{background: #fff!important;} -#theme12 .layui-form-radio:hover *,#theme12 .layui-form-radioed,#theme12 .layui-form-radioed>i{color: #1e9fff} - - -#theme13 .layui-btn, -#theme13 .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme13 .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme13 .layui-form-onswitch, -#theme13 .fun-layer-class .layui-layer-title {background:#963885!important;color: #fff;border:none;} #theme13 blockquote{border-left-color:#963885 ;} -#theme13 .layui-input:focus, #theme13 .layui-textarea:focus {border-color: #963885!important;} -#theme13 .layui-laydate-main .layui-laydate-header{background: #963885!important;} -#theme13 .layui-laydate-main .layui-this{background: #963885!important;} -#theme13 .layui-laydate-footer span:hover{color: #963885!important;} -#theme13 .layui-form-radio>i:hover, #theme13 .layui-form-radioed>i, #theme13 .layui-form-checked i, #theme13 .layui-form-checked:hover i {color: #963885;} -#theme13.layui-form-checked span,#theme13 .layui-form-checked:hover span{background: #963885!important;} -#theme13 .fun-layer-class .layui-layer-title {background:#963885!important;color: #fff;} -#theme13 .layui-form-select dl dd.layui-this{ background: #963885!important;} -#theme13 .multi dl dd.layui-this{ background: none!important;} -#theme13 .multi .layui-form-checked span, #theme13 .multi .layui-form-checked:hover span{background: none!important;} -#theme13 .multi .layui-form-checked[lay-skin=primary] i{background: #963885!important;} -#theme13 .multi .layui-form-radio>i:hover, #theme13 .multi .layui-form-radioed>i, #theme13 .multi .layui-form-checked i, -#theme13 .multi .layui-form-checked:hover i{color: #fff} -#theme13 .multi .layui-form-checked span,#theme13 .multi .layui-form-checked:hover span{background: #fff!important;} -#theme13 .layui-form-radio:hover *,#theme13 .layui-form-radioed,#theme13 .layui-form-radioed>i{color: #963885} - -#theme14 .layui-btn, -#theme14 .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme14 .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme14 .layui-form-onswitch, -#theme14 .fun-layer-class .layui-layer-title {background:#963885!important;color: #fff;border:none;} #theme14 blockquote{border-left-color:#963885 ;} -#theme14 .layui-input:focus, #theme14 .layui-textarea:focus {border-color: #963885!important;} -#theme14 .layui-laydate-main .layui-laydate-header{background: #963885!important;} -#theme14 .layui-laydate-main .layui-this{background: #963885!important;} -#theme14 .layui-laydate-footer span:hover{color: #963885!important;} -#theme14 .layui-form-radio>i:hover, #theme14 .layui-form-radioed>i, #theme14 .layui-form-checked i, #theme14 .layui-form-checked:hover i {color: #963885;} -#theme14 .layui-form-checked span,#theme14 .layui-form-checked:hover span{background: #963885!important;} -#theme14 .fun-layer-class .layui-layer-title {background:#963885!important;color: #fff;} -#theme144 .layui-form-select dl dd.layui-this{ background: #963885!important;} -#theme144 .multi dl dd.layui-this{ background: none!important;} -#theme14 .multi .layui-form-checked span, #theme14 .multi .layui-form-checked:hover span{background: none!important;} -#theme14 .multi .layui-form-checked[lay-skin=primary] i{background: #963885!important;} -#theme14 .multi .layui-form-radio>i:hover, #theme14 .multi .layui-form-radioed>i, #theme14 .multi .layui-form-checked i, -#theme14 .multi .layui-form-checked:hover i{color: #fff} -#theme14 .multi .layui-form-checked span,#theme14 .multi .layui-form-checked:hover span{background: #fff!important;} -#theme14 .layui-form-radio:hover *,#theme14 .layui-form-radioed,#theme14 .layui-form-radioed>i{color: #963885} - - -#theme15 .layui-btn, -#theme15 .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme15 .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme15 .layui-form-onswitch, -#theme15 .fun-layer-class .layui-layer-title {background:#1e9fff!important;color: #fff;border:none;} #theme15 blockquote{border-left-color:#1e9fff ;} -#theme15 .layui-input:focus, #theme15 .layui-textarea:focus {border-color: #1e9fff!important;} -#theme15 .layui-laydate-main .layui-laydate-header{background: #1e9fff!important;} -#theme15 .layui-laydate-main .layui-this{background: #1e9fff!important;} -#theme15 .layui-laydate-footer span:hover{color: #1e9fff!important;} -#theme15 .layui-form-radio>i:hover, #theme15 .layui-form-radioed>i, #theme15 .layui-form-checked i, #theme15 .layui-form-checked:hover i {color: #1e9fff;} -#theme15 .layui-form-checked span,#theme15 .layui-form-checked:hover span{background: #1e9fff!important;} -#theme15 .fun-layer-class .layui-layer-title {background:#1e9fff!important;color: #fff;} -#theme15 .layui-form-select dl dd.layui-this{ background: #1e9fff!important;} -#theme15 .multi dl dd.layui-this{ background: none!important;} -#theme15 .multi .layui-form-checked span, #theme15 .multi .layui-form-checked:hover span{background: none!important;} -#theme15 .multi .layui-form-checked[lay-skin=primary] i{background: #1e9fff!important;} -#theme15 .multi .layui-form-radio>i:hover, #theme15 .multi .layui-form-radioed>i, #theme15 .multi .layui-form-checked i, -#theme15 .multi .layui-form-checked:hover i{color: #fff} -#theme15 .multi .layui-form-checked span,#theme15 .multi .layui-form-checked:hover span{background: #fff!important;} -#theme15 .layui-form-radio:hover *,#theme15 .layui-form-radioed,#theme15 .layui-form-radioed>i{color: #1e9fff} - -#theme16 .layui-btn, -#theme16 .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme16 .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme16 .layui-form-onswitch, -#theme16 .fun-layer-class .layui-layer-title {background:#ffb800!important;color: #fff;border:none;} #theme16 blockquote{border-left-color:#ffb800 ;} -#theme16 .layui-input:focus, #theme16 .layui-textarea:focus {border-color: #ffb800!important;} -#theme16 .layui-laydate-main .layui-laydate-header{background: #ffb800!important;} -#theme16 .layui-laydate-main .layui-this{background: #ffb800!important;} -#theme16 .layui-laydate-footer span:hover{color: #ffb800!important;} -#theme16 .layui-form-radio>i:hover, #theme16 .layui-form-radioed>i, #theme16 .layui-form-checked i, #theme16 .layui-form-checked:hover i {color: #ffb800;} -#theme16 .layui-form-checked span,#theme16 .layui-form-checked:hover span{background: #ffb800!important;} -#theme16 .fun-layer-class .layui-layer-title {background:#ffb800!important;color: #fff;} -#theme16 .layui-form-select dl dd.layui-this{ background: #ffb800!important;} -#theme16 .multi dl dd.layui-this{ background: none!important;} -#theme16 .multi .layui-form-checked span, #theme16 .multi .layui-form-checked:hover span{background: none!important;} -#theme16 .multi .layui-form-checked[lay-skin=primary] i{background: #ffb800!important;} -#theme16 .multi .layui-form-radio>i:hover, #theme16 .multi .layui-form-radioed>i, #theme16 .multi .layui-form-checked i, -#theme16 .multi .layui-form-checked:hover i{color: #fff} -#theme16 .multi .layui-form-checked span,#theme16 .multi .layui-form-checked:hover span{background: #fff!important;} -#theme16 .layui-form-radio:hover *,#theme16 .layui-form-radioed,#theme16 .layui-form-radioed>i{color: #ffb800} - -#theme17 .layui-btn, -#theme17 .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme17 .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme17 .layui-form-onswitch, -#theme17 .fun-layer-class .layui-layer-title {background:#ffb800!important;color: #fff;border:none;} #theme17 blockquote{border-left-color:#ffb800 ;} -#theme17 .layui-input:focus, #theme17 .layui-textarea:focus {border-color: #ffb800!important;} -#theme17 .layui-laydate-main .layui-laydate-header{background: #ffb800!important;} -#theme17 .layui-laydate-main .layui-this{background: #ffb800!important;} -#theme17 .layui-laydate-footer span:hover{color: #ffb800!important;} -#theme17 .layui-form-radio>i:hover, #theme17 .layui-form-radioed>i, #theme17 .layui-form-checked i, #theme17 .layui-form-checked:hover i {color: #ffb800;} -#theme17 .layui-form-checked span,#theme17 .layui-form-checked:hover span{background: #ffb800!important;} -#theme17 .fun-layer-class .layui-layer-title {background:#ffb800!important;color: #fff;} -#theme17 .layui-form-select dl dd.layui-this{ background: #ffb800!important;} -#theme17 .multi dl dd.layui-this{ background: none!important;} -#theme17 .multi .layui-form-checked span, #theme17 .multi .layui-form-checked:hover span{background: none!important;} -#theme17 .multi .layui-form-checked[lay-skin=primary] i{background: #ffb800!important;} -#theme17 .multi .layui-form-radio>i:hover, #theme17 .multi .layui-form-radioed>i, #theme17 .multi .layui-form-checked i, -#theme17 .multi .layui-form-checked:hover i{color: #fff} -#theme17 .multi .layui-form-checked span,#theme17 .multi .layui-form-checked:hover span{background: #fff!important;} -#theme17 .layui-form-radio:hover *,#theme17 .layui-form-radioed,#theme17 .layui-form-radioed>i{color: #ffb800} - - -#theme18 .layui-btn, -#theme18 .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme18 .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme18 .layui-form-onswitch, -#theme18 .fun-layer-class .layui-layer-title {background:#e82121!important;color: #fff;border:none;} #theme18 blockquote{border-left-color:#e82121 ;} -#theme18 .layui-input:focus, #theme18 .layui-textarea:focus {border-color: #e82121!important;} -#theme18 .layui-laydate-main .layui-laydate-header{background: #e82121!important;} -#theme18 .layui-laydate-main .layui-this{background: #e82121!important;} -#theme18 .layui-laydate-footer span:hover{color: #e82121!important;} -#theme18 .layui-form-radio>i:hover, #theme18 .layui-form-radioed>i, #theme18 .layui-form-checked i, #theme18 .layui-form-checked:hover i {color: #e82121;} -#theme18 .layui-form-checked span,#theme18 .layui-form-checked:hover span{background: #e82121!important;} -#theme18 .fun-layer-class .layui-layer-title {background:#e82121!important;color: #fff;} -#theme18 .layui-form-select dl dd.layui-this{ background: #e82121!important;} -#theme18 .multi dl dd.layui-this{ background: none!important;} -#theme18 .multi .layui-form-checked span, #theme18 .multi .layui-form-checked:hover span{background: none!important;} -#theme18 .multi .layui-form-checked[lay-skin=primary] i{background: #e82121!important;} -#theme18 .multi .layui-form-radio>i:hover, #theme18 .multi .layui-form-radioed>i, #theme18 .multi .layui-form-checked i, -#theme18 .multi .layui-form-checked:hover i{color: #fff} -#theme18 .multi .layui-form-checked span,#theme18 .multi .layui-form-checked:hover span{background: #fff!important;} -#theme18 .layui-form-radio:hover *,#theme18 .layui-form-radioed,#theme18 .layui-form-radioed>i{color: #e82121} - - -#theme19 .layui-btn, -#theme19 .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme19 .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme19 .layui-form-onswitch, -#theme19 .fun-layer-class .layui-layer-title {background:#e82121!important;color: #fff;border:none;} #theme19 blockquote{border-left-color:#e82121 ;} -#theme19 .layui-input:focus, #theme19 .layui-textarea:focus {border-color: #e82121!important;} -#theme19 .layui-laydate-main .layui-laydate-header{background: #e82121!important;} -#theme19 .layui-laydate-main .layui-this{background: #e82121!important;} -#theme19 .layui-laydate-footer span:hover{color: #e82121!important;} -#theme19 .layui-form-radio>i:hover, #theme19 .layui-form-radioed>i, #theme19 .layui-form-checked i, #theme19 .layui-form-checked:hover i {color: #e82121;} -#theme19 .layui-form-checked span,#theme19 .layui-form-checked:hover span{background: #e82121!important;} -#theme19 .fun-layer-class .layui-layer-title {background:#e82121!important;color: #fff;} -#theme19 .layui-form-select dl dd.layui-this{ background: #e82121!important;} -#theme19 .multi dl dd.layui-this{ background: none!important;} -#theme19 .multi .layui-form-checked span, #theme19 .multi .layui-form-checked:hover span{background: none!important;} -#theme19 .multi .layui-form-checked[lay-skin=primary] i{background: #e82121!important;} -#theme19 .multi .layui-form-radio>i:hover, #theme19 .multi .layui-form-radioed>i, #theme19 .multi .layui-form-checked i, -#theme19 .multi .layui-form-checked:hover i{color: #fff} -#theme19 .multi .layui-form-checked span,#theme19 .multi .layui-form-checked:hover span{background: #fff!important;} -#theme19 .layui-form-radio:hover *,#theme19 .layui-form-radioed,#theme19 .layui-form-radioed>i{color: #e82121} - - -#theme20 .layui-btn, -#theme20 .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme20 .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme20 .layui-form-onswitch, -#theme20 .fun-layer-class .layui-layer-title {background: #4d70ff!important;color: #fff;border:none;} #theme20 blockquote{border-left-color: #4d70ff;} -#theme20 .layui-input:focus, #theme20 .layui-textarea:focus {border-color: #4d70ff!important;} -#theme20 .layui-laydate-main .layui-laydate-header{background: #4d70ff!important;} -#theme20 .layui-laydate-main .layui-this{background: #4d70ff!important;} -#theme20 .layui-laydate-footer span:hover{color: #4d70ff!important;} -#theme20 .layui-form-radio>i:hover, #theme20 .layui-form-radioed>i, #theme20 .layui-form-checked i, #theme20 .layui-form-checked:hover i {color: #4d70ff;} -#theme20 .layui-form-checked span,#theme20 .layui-form-checked:hover span{background: #4d70ff!important;} -#theme20 .fun-layer-class .layui-layer-title {background:#4d70ff!important;color: #fff;} -#theme20 .layui-form-select dl dd.layui-this{ background: #4d70ff!important;} -#theme20 .multi dl dd.layui-this{ background: none!important;} -#theme20 .multi .layui-form-checked span, #theme20 .multi .layui-form-checked:hover span{background: none!important;} -#theme20 .multi .layui-form-checked[lay-skin=primary] i{background: #4d70ff!important;} -#theme20 .multi .layui-form-radio>i:hover, #theme20 .multi .layui-form-radioed>i, #theme20 .multi .layui-form-checked i, -#theme20 .multi .layui-form-checked:hover i{color: #fff} -#theme20 .multi .layui-form-checked span,#theme20 .multi .layui-form-checked:hover span{background: #fff!important;} -#theme20 .multi .layui-form-checked span,#theme20 .multi .layui-form-checked:hover span{background: #fff!important;} -#theme20 .layui-form-radio:hover *,#theme20 .layui-form-radioed,#theme20 .layui-form-radioed>i{color: #4d70ff!important} +@import url("../../common/css/iconfont.css"); + +/* ============================================ + CSS 变量定义 - 主题颜色 + ============================================ */ +:root { + /* 默认主题色 */ + --theme-primary: #4d70ff; + + /* 主题色映射 */ + --theme-0: #4d70ff; + --theme-1: #20b598; + --theme-2: #8e10b9; + --theme-3: #b8279f; + --theme-4: #20b598; + --theme-5: #8e10b9; + --theme-6: #20b598; + --theme-7: #20b598; + --theme-8: #00a65a; + --theme-9: #8e10b9; + --theme-10: #00a65a; + --theme-11: #ffa4d1; + --theme-12: #1e9fff; + --theme-13: #963885; + --theme-14: #963885; + --theme-15: #1e9fff; + --theme-16: #ffb800; + --theme-17: #ffb800; + --theme-18: #e82121; + --theme-19: #e82121; + --theme-20: #4d70ff; + + /* 通用颜色 */ + --color-placeholder: #ccc; + --color-danger: #f44336; + --bg-body: #f1f4f6; + --bg-container: #fff; + --bg-table-even: rgb(243 245 243); + --bg-table-hover: #eeeeee; + --bg-layer-title: rgb(243, 246, 246); + --bg-layer-btn: #fff; + --border-layer-btn: #dedede; + --text-layer-btn: #333; +} + +/* ============================================ + 基础样式 + ============================================ */ +::-webkit-input-placeholder { + color: var(--color-placeholder); + font-size: 12px; +} + +::-webkit-scrollbar { + width: 8px; + height: 10px; + background: #ffffff; + cursor: pointer; + overflow-y: auto; +} + +::-webkit-scrollbar-thumb { + background-color: #9C9DA098; + border-radius: 2em; +} + +::-webkit-scrollbar-track { + box-shadow: inset 0 0 5px rgba(240, 240, 240, 0.5); + border-radius: 0; + background: rgba(240, 240, 240, 0.5); + cursor: pointer; +} + +html { + -ms-overflow-style: none; + overflow: -moz-scrollbars-none; +} + +body { + background: var(--bg-body); + padding: 10px; +} + +/* 子页面主题 */ +body .layui-form-onswitch { + border: none; +} + +.layui-form-checked[lay-skin=primary] i { + border: none; +} + +.fun-container { + background: var(--bg-container); +} + +.layui-table, +.layui-table-view { + margin: 0 auto; +} + +.fl { + float: left; +} + +.fr { + float: right; +} + +.clearfix { + clear: both; +} + +.layui-table-tool { + z-index: 80; +} + +.layui-table-tool-temp { + padding-right: 120px; +} + +.layui-form-item .layui-form-mid.layui-word-aux { + float: none; +} + +.layui-form-item .required::after { + content: "*"; + color: red; + position: absolute; + margin-left: 4px; + font-weight: bold; + line-height: 1.8em; + top: 6px; + right: 5px; +} + +.layui-input:focus, +.layui-textarea:focus { + border-color: var(--theme-primary); +} + +.layui-text-center, +.layui-btn-center { + text-align: center; +} + +.layui-text-left { + text-align: left; +} + +.layui-text-right { + text-align: right; +} + +.layui-upload-list li { + display: inline-block; +} + +.layui-upload-list li i { + background: #f2f2f2; + cursor: pointer; +} + +.layui-search-fieldset form { + padding: 10px; +} + +.layui-search-fieldset .layui-btn { + height: 32px; + line-height: 28px; + font-size: 12px; + padding: 0 10px; +} + +.layui-search-fieldset .layui-form-label { + padding: 0 8px; + height: 32px; + line-height: 30px; + text-align: left; +} + +.layui-search-fieldset .layui-input-inline input, +.layui-search-fieldset .layui-input-inline select { + width: 100%; + height: 32px; + padding: 2px 8px; + line-height: 1em; +} + +.layui-search-fieldset .layui-between .layui-input-inline { + margin-right: 3%; + width: 30%; +} + +.layui-search-fieldset .layui-between .layui-input-inline + .layui-input-inline:before { + content: "-"; + position: absolute; + left: -6%; + top: 6px; +} + +.layui-search-fieldset .layui-form-select dl { + top: 31px; + padding: 0; +} + +.layui-form-select dl dd.layui-this { + background: var(--theme-primary); + color: #fff; +} + +.layui-form-checked span, +.layui-form-checked:hover span { + background-color: var(--theme-primary); +} + +.fun-layer-class .layui-layer-btn a:last-child { + background: var(--bg-layer-btn); + color: var(--text-layer-btn); + border: 1px solid var(--border-layer-btn); +} + +.fun-layer-class .layui-layer-title { + background: var(--bg-layer-title); + border-bottom: none; +} + +.fun-layer-class .layui-layer-btn.layui-layer-btn-c { + background-color: var(--bg-layer-title); +} + +.layui-form-item.layui-btn-center button.submit { + background: linear-gradient(0deg, rgba(0, 172, 238, 1) 0%, rgba(2, 126, 251, 1) 100%); + color: #fff; + border: none; +} + +.layui-form-item.layui-btn-center button.reset { + background: #eee; +} + +.layui-form-item.layui-btn-center button:hover { + border-color: #eee; +} + +/* 表格样式 */ +.layui-table tr:nth-child(even) { + background-color: var(--bg-table-even); +} + +.layui-table tbody tr:hover, +.layui-table-hover { + background-color: var(--bg-table-hover); +} + +.layui-form-item input[lay-filter=date] { + padding-left: 36px; +} + +.layui-form-item .layui-icon.layui-icon-date { + color: var(--color-danger); +} + +.layui-form-radio:hover *, +.layui-form-radioed, +.layui-form-radioed>i { + color: var(--theme-primary); +} + +.layui-form-checked[lay-skin=primary] i { + border-color: var(--theme-primary); + background-color: var(--theme-primary); +} + +/* ============================================ + 主题样式 - 使用 CSS 变量统一管理 + ============================================ */ +/* 主题变量映射 - 使用循环方式定义(浏览器原生不支持,但更清晰) */ +[id^="theme"] { + --theme-color: var(--theme-0); +} + +/* 各主题颜色映射 */ +#theme0 { --theme-color: var(--theme-0); } +#theme1 { --theme-color: var(--theme-1); } +#theme2 { --theme-color: var(--theme-2); } +#theme3 { --theme-color: var(--theme-3); } +#theme4 { --theme-color: var(--theme-4); } +#theme5 { --theme-color: var(--theme-5); } +#theme6 { --theme-color: var(--theme-6); } +#theme7 { --theme-color: var(--theme-7); } +#theme8 { --theme-color: var(--theme-8); } +#theme9 { --theme-color: var(--theme-9); } +#theme10 { --theme-color: var(--theme-10); } +#theme11 { --theme-color: var(--theme-11); } +#theme12 { --theme-color: var(--theme-12); } +#theme13 { --theme-color: var(--theme-13); } +#theme14 { --theme-color: var(--theme-14); } +#theme15 { --theme-color: var(--theme-15); } +#theme16 { --theme-color: var(--theme-16); } +#theme17 { --theme-color: var(--theme-17); } +#theme18 { --theme-color: var(--theme-18); } +#theme19 { --theme-color: var(--theme-19); } +#theme20 { --theme-color: var(--theme-20); } + +/* 统一主题样式规则 */ +[id^="theme"] .layui-btn, +[id^="theme"] .fun-layer-class .layui-layer-btn .layui-layer-btn0, +[id^="theme"] .layui-laypage .layui-laypage-curr .layui-laypage-em, +[id^="theme"] .layui-form-onswitch, +[id^="theme"] .fun-layer-class .layui-layer-title { + background: var(--theme-color); + color: #fff; + border: none; +} + +[id^="theme"] blockquote { + border-left-color: var(--theme-color); +} + +[id^="theme"] .layui-input:focus, +[id^="theme"] .layui-textarea:focus { + border-color: var(--theme-color); +} + +[id^="theme"] .layui-laydate-main .layui-laydate-header, +[id^="theme"] .layui-laydate-main .layui-this { + background: var(--theme-color); +} + +[id^="theme"] .layui-laydate-footer span:hover { + color: var(--theme-color); +} + +[id^="theme"] .layui-form-radio>i:hover, +[id^="theme"] .layui-form-radioed>i, +[id^="theme"] .layui-form-checked i, +[id^="theme"] .layui-form-checked:hover i { + color: var(--theme-color); +} + +[id^="theme"] .layui-form-select .layui-form-checked span, +[id^="theme"] .layui-form-select .layui-form-checked:hover span, +[id^="theme"] .layui-form-checked span, +[id^="theme"] .layui-form-checked:hover span { + background-color: var(--theme-color); +} + +[id^="theme"] .fun-layer-class .layui-layer-title { + background: var(--theme-color); + color: #fff; +} + +[id^="theme"] .layui-form-select dl dd.layui-this { + background: var(--theme-color); +} + +/* 多选特殊样式 */ +[id^="theme"] .multi dl dd.layui-this { + background: none; +} + +[id^="theme"] .multi .layui-form-checked span, +[id^="theme"] .multi .layui-form-checked:hover span { + background: none; +} + +[id^="theme"] .multi .layui-form-checked[lay-skin=primary] i { + background: var(--theme-color); +} + +[id^="theme"] .multi .layui-form-radio>i:hover, +[id^="theme"] .multi .layui-form-radioed>i, +[id^="theme"] .multi .layui-form-checked i, +[id^="theme"] .multi .layui-form-checked:hover i { + color: #fff; +} + +[id^="theme"] .multi .layui-form-checked span, +[id^="theme"] .multi .layui-form-checked:hover span { + background: #fff; +} + +/* 特殊主题样式覆盖 */ +#theme2 .layui-laydate-main .layui-laydate-header, +#theme2 .layui-laydate-main .layui-this { + background: var(--theme-1); +} + +#theme2 .layui-laydate-footer span:hover { + color: var(--theme-1); +} + +#theme10 .layui-form-select dl dd.layui-this { + background: var(--theme-1); +} + +#theme10 .multi .layui-form-checked[lay-skin=primary] i { + background: var(--theme-1); +} + +#theme10 .layui-form-radio:hover *, +#theme10 .layui-form-radioed, +#theme10 .layui-form-radioed>i { + color: var(--theme-1); +} + +#theme12.layui-form-checked span, +#theme12 .layui-form-checked:hover span { + background: var(--theme-12); +} + +#theme13.layui-form-checked span, +#theme13 .layui-form-checked:hover span { + background: var(--theme-13); +} + +#theme20 .layui-form-radio:hover *, +#theme20 .layui-form-radioed, +#theme20 .layui-form-radioed>i { + color: var(--theme-20); +} + +/* 修复 theme14 的拼写错误 */ +#theme14 .layui-form-select dl dd.layui-this { + background: var(--theme-14); +} + +#theme14 .multi dl dd.layui-this { + background: none; +} diff --git a/public/static/backend/css/fun.css b/public/static/backend/css/fun.css index 56c0917a5..e1d74dc26 100644 --- a/public/static/backend/css/fun.css +++ b/public/static/backend/css/fun.css @@ -11,26 +11,23 @@ body .layui-layer-content, .layui-layer.layui-layer-dialog .layui-layer-btn { text-align: center } +/* 颜色工具类 - 使用 CSS 变量 */ body .layui-blue { - color: #4d70ff !important + color: var(--theme-primary, #4d70ff); } body .layui-red { - color: #dd4b39 !important + color: #dd4b39; } body .layui-yellow { - color: #f39c12 !important + color: #f39c12; } - -body .fun-layer-class .layui-layer-btn a:last-child{background:#fff;color:#333;border:1px solid #dedede;} -body .fun-layer-class .layui-layer-title{background:rgb(243, 246, 246);border-bottom: none;} -body .fun-layer-class .layui-layer-btn.layui-layer-btn-c{background-color:rgb(243, 246, 246)} -body .layui-form-item.layui-btn-center button.submit{background: linear-gradient(0deg, rgba(0,172,238,1) 0%, rgba(2,126,251,1) 100%);color:#fff;border:none} -body .layui-form-item.layui-btn-center button.reset{background:#eee} -body .layui-form-item.layui-btn-center button:hover{border-color:#eee;} - +/* 注意:fun-layer-class 样式已在 comm.css 中定义,这里移除重复 */ +.layui-tabs *{ + font-style: normal; +} .layui-layout-admin .layui-header { position: fixed; top: 0; @@ -43,17 +40,14 @@ body .layui-form-item.layui-btn-center button:hover{border-color:#eee;} } .layui-header .layui-nav { - padding: 0 !important -} - -.layui-header .layui-layout-left { - left: 255px + padding: 0; } +/* 合并重复的 .layui-header .layui-layout-left 定义 */ .layui-header .layui-layout-left { - position: absolute !important; + position: absolute; top: 0; - left: 220px + left: 220px; } .layui-header .layui-layout-center { @@ -117,7 +111,7 @@ body .layui-form-item.layui-btn-center button:hover{border-color:#eee;} } .layui-layout-admin .layui-header ul li dl dd a { - color: #595959 !important + color: #595959; } .layui-layout-admin .layui-logo { @@ -169,8 +163,10 @@ body .layui-form-item.layui-btn-center button:hover{border-color:#eee;} transition: all .3s ease } -.layui-nav-itemed > a, .layui-nav-tree .layui-nav-title a, .layui-nav-tree .layui-nav-title a:hover { - color: #fff !important +.layui-nav-itemed > a, +.layui-nav-tree .layui-nav-title a, +.layui-nav-tree .layui-nav-title a:hover { + color: #fff; } .layui-side-shrink .layui-layout-admin .layui-logo cite { @@ -184,8 +180,8 @@ body .layui-form-item.layui-btn-center button:hover{border-color:#eee;} position: fixed; left: 0; top: 0; - width: 220px !important; - z-index: 1001 + width: 220px; + z-index: 1001; } .layui-layout-admin .layui-side-menu .layui-side-scroll { @@ -211,29 +207,30 @@ body .layui-form-item.layui-btn-center button:hover{border-color:#eee;} padding-top: 0; } +/* 菜单嵌套层级样式 - 使用递增的 padding-left */ .layui-side-menu .layui-nav .layui-nav-item .layui-nav-child a { - padding-left:35px; + padding-left: 35px; padding-right: 30px; } .layui-side-menu .layui-nav .layui-nav-item .layui-nav-child .layui-nav-child a { - padding-left: 45px + padding-left: 45px; } .layui-side-menu .layui-nav .layui-nav-child .layui-nav-child .layui-nav-child a { - padding-left: 55px + padding-left: 55px; } .layui-side-menu .layui-nav .layui-nav-child .layui-nav-child .layui-nav-child .layui-nav-child a { - padding-left: 65px + padding-left: 65px; } .layui-side-menu .layui-nav .layui-nav-child .layui-nav-child .layui-nav-child .layui-nav-child .layui-nav-child a { - padding-left: 75px + padding-left: 75px; } .layui-side-menu .layui-nav .layui-nav-child .layui-nav-child .layui-nav-child .layui-nav-child .layui-nav-child .layui-nav-child a { - padding-left: 95px + padding-left: 95px; } @@ -256,45 +253,24 @@ body .layui-form-item.layui-btn-center button:hover{border-color:#eee;} box-shadow: 0 1px 2px 0 rgba(0, 0, 0, .1) } -.layui-layout-admin .layui-pagetabs .layui-tabs-control { - position: absolute; - top: 0; - width: 40px; - height: 40px; - line-height: 40px; - text-align: center; - cursor: pointer; - transition: all .3s; - -webkit-transition: all .3s; - box-sizing: border-box; - border-left: 1px solid #f6f6f6; - background: #fff -} - -.layui-layout-admin .layui-pagetabs .layui-tabs-control:hover { - background-color: #f6f6f6 -} - -.layui-layout-admin .layui-pagetabs .layui-icon-prev { - left: 0; - border-left: none; - border-right: 1px solid #f6f6f6 +.layui-layout-admin .layui-pagetabs .layui-tabs { + height: calc(100vh - 120px); /* 根据实际布局调整 */ + display: flex; + flex-direction: column; + height: 100%; margin-bottom:30px; + font-style: normal; } -.layui-layout-admin .layui-pagetabs .layui-icon-next { - right: 40px +.layui-layout-admin .layui-pagetabs .layui-tabs .layui-tabs-item{ + height: 100%; + margin-bottom:60px; + /*overflow: auto;*/ } .layui-layout-admin .layui-pagetabs .layui-icon-down { right: 0 } -.layui-layout-admin .layui-pagetabs .layui-tab { - margin: 0; - height: 100%; - width: 100%; - overflow: hidden -} .layui-layout-admin .layui-tabs-select.layui-nav { position: absolute; @@ -324,9 +300,10 @@ body .layui-form-item.layui-btn-center button:hover{border-color:#eee;} right: 0 } -.layui-layout-admin .layui-tabs-select.layui-nav .layui-nav-child dd.layui-this, .layui-layout-admin .layui-tabs-select.layui-nav .layui-nav-child dd.layui-this a { - background-color: #f2f2f2 !important; - color: #333 +.layui-layout-admin .layui-tabs-select.layui-nav .layui-nav-child dd.layui-this, +.layui-layout-admin .layui-tabs-select.layui-nav .layui-nav-child dd.layui-this a { + background-color: #f2f2f2; + color: #333; } .layui-nav-tree .layui-nav-more:before { @@ -370,23 +347,23 @@ body .layui-form-item.layui-btn-center button:hover{border-color:#eee;} display: none } -.layui-layout-admin .layui-tab-title { - position: absolute; - left: 40px; - right: 80px; - background: #fff -} +/*.layui-layout-admin .layui-tabs-header {*/ +/* position: absolute;*/ +/* left: 40px;*/ +/* right: 80px;*/ +/* background: #fff*/ +/*}*/ -/*.layui-layout-admin .layui-tab-title i {*/ +/*.layui-layout-admin .layui-tabs-header i {*/ /* vertical-align: middle*/ /*}*/ -.layui-layout-admin .layui-tab-title li:hover, .layui-pagetabs .layui-tab-title li.layui-this { +.layui-layout-admin .layui-tabs-header li:hover, .layui-pagetabs .layui-tabs-header li.layui-this { background-color: #f6f6f6; border-top: 1px } -.layui-pagetabs .layui-tab-title li { +.layui-pagetabs .layui-tabs-header li { min-width: 0; line-height: 40px; max-width: 160px; @@ -396,12 +373,13 @@ body .layui-form-item.layui-btn-center button:hover{border-color:#eee;} vertical-align: top } -.layui-pagetabs .layui-tab-title li:hover, .layui-pagetabs .layui-tab-title li.layui-this { +.layui-pagetabs .layui-tabs-header li:hover, +.layui-pagetabs .layui-tabs-header li.layui-this { background-color: rgba(0, 0, 0, 0.02); - color: #18bc9c !important + color: #18bc9c; } -.layui-pagetabs .layui-tab-title li.layui-this:after { +.layui-pagetabs .layui-tabs-header li.layui-this:after { content: ''; position: absolute; top: 0; @@ -414,7 +392,7 @@ body .layui-form-item.layui-btn-center button:hover{border-color:#eee;} transition: all .3s; -webkit-transition: all .3s } -.layui-pagetabs .layui-tab-title li .layui-tab-icon-active { +.layui-pagetabs .layui-tabs-header li .layui-tab-icon-active { display: inline-block; background-color: lightgray; width: 9px; @@ -422,33 +400,13 @@ body .layui-form-item.layui-btn-center button:hover{border-color:#eee;} border-radius: 30px; margin-right: 5px; } -.layui-pagetabs .layui-tab-title li.layui-this .layui-tab-icon-active { +.layui-pagetabs .layui-tabs-header li.layui-this .layui-tab-icon-active { background-color: #4d70ff; } -.layui-pagetabs .layui-tab-title li:first-child { +.layui-pagetabs .layui-tabs-header li:first-child { padding-right: 15px } -.layui-layout-admin .layui-pagetabs .layui-tab-title li:first-child .layui-tab-close { - display: none -} - -.layui-layout-admin .layui-pagetabs .layui-tab-title li .layui-tab-close { - border-radius: 50% -} - -.layui-layout-admin .layui-pagetabs .layui-tab-title li .layui-tab-close:hover { - border-radius: 50% -} - -.layui-layout-admin .layui-pagetabs .layui-tab-title li cite { - font-style: normal; - padding-left: 5px -} - -.layui-layout-admin .layui-tab-title .layui-tab-bar { - display: none -} .layui-layout-admin .layui-body-shade { position: fixed; @@ -458,7 +416,9 @@ body .layui-form-item.layui-btn-center button:hover{border-color:#eee;} top: 0; bottom: 0; background-color: rgba(0, 0, 0, .3); - z-index: 1000 + z-index: 1000; + flex: 1; + min-height: 0; /* 修复Firefox兼容问题 */ } .layui-layout-admin .layui-body { @@ -468,6 +428,7 @@ body .layui-form-item.layui-btn-center button:hover{border-color:#eee;} margin-bottom: 45px; position: absolute; z-index: auto; + /*height: 100%;*/ /*overflow: auto;*/ -webkit-transition: left .3s; transition: left .3s; @@ -488,7 +449,7 @@ body .layui-form-item.layui-btn-center button:hover{border-color:#eee;} font-size: 12px } -.layui-body.layui-tab-content .layui-tab-item.layui-show { +.layui-body.layui-tabs-body .layui-tabs-item.layui-show { width: 100%; height: 98%; padding: 0 @@ -556,28 +517,17 @@ body .layui-form-item.layui-btn-center button:hover{border-color:#eee;} display: none !important } +/* 合并重复的嵌套选择器 - 使用通用规则 */ .layui-side-shrink .layui-side-menu .layui-nav .layui-nav-item .layui-nav-child a { - padding: 0 20px -} - -.layui-side-shrink .layui-side-menu .layui-nav .layui-nav-item .layui-nav-child .layui-nav-child a { - padding-left: 20px -} - -.layui-side-shrink .layui-side-menu .layui-nav .layui-nav-child .layui-nav-child .layui-nav-child a { - padding-left: 20px -} - -.layui-side-shrink .layui-side-menu .layui-nav .layui-nav-child .layui-nav-child .layui-nav-child .layui-nav-child a { - padding-left: 20px -} - -.layui-side-shrink .layui-side-menu .layui-nav .layui-nav-child .layui-nav-child .layui-nav-child .layui-nav-child .layui-nav-child a { - padding-left: 20px + padding: 0 20px; } +.layui-side-shrink .layui-side-menu .layui-nav .layui-nav-child .layui-nav-child a, +.layui-side-shrink .layui-side-menu .layui-nav .layui-nav-child .layui-nav-child .layui-nav-child a, +.layui-side-shrink .layui-side-menu .layui-nav .layui-nav-child .layui-nav-child .layui-nav-child .layui-nav-child a, +.layui-side-shrink .layui-side-menu .layui-nav .layui-nav-child .layui-nav-child .layui-nav-child .layui-nav-child .layui-nav-child a, .layui-side-shrink .layui-side-menu .layui-nav .layui-nav-child .layui-nav-child .layui-nav-child .layui-nav-child .layui-nav-child .layui-nav-child a { - padding-left: 20px + padding-left: 20px; } .layui-side-shrink .layui-side-menu .layui-nav-item.layui-nav-hover .layui-nav-child-drop { @@ -647,9 +597,10 @@ body .layui-form-item.layui-btn-center button:hover{border-color:#eee;} -webkit-transform: translate(-50%, -50%) } -.fun-loading .loading > span, .signal-loader > span { - background-color: #4d70ff; - display: inline-block +.fun-loading .loading > span, +.signal-loader > span { + background-color: var(--theme-primary, #4d70ff); + display: inline-block; } .fun-loading .loading > span:nth-child(1), .loading.sm > span:nth-child(1), .signal-loader > span:nth-child(1), .signal-loader.sm > span:nth-child(1) { @@ -834,7 +785,7 @@ body .layui-form-item.layui-btn-center button:hover{border-color:#eee;} .layui-side-shrink .layui-layout-admin .layui-body { width: 100%; - height: 100%; + /*height: 100%;*/ position: fixed; left: 0; top: 40px diff --git a/public/static/backend/css/global.css b/public/static/backend/css/global.css index 5d47ffe8e..2cfdcc0a6 100644 --- a/public/static/backend/css/global.css +++ b/public/static/backend/css/global.css @@ -1,40 +1,95 @@ -.layui-form-radio:hover *, .layui-form-radioed, .layui-form-radioed>i{color:#4d70ff} -.layui-form-onswitch,.layui-laypage .layui-laypage-curr .layui-laypage-em{background-color:#4d70ff;} -.layui-elem-quote{border-left: 5px solid #4d70ff} -.layui-bg-yellow{background-color:#E6A23C;} -.layui-bg-pink{background-color:#FF69B4;} -.layui-bg-purple{background-color:#A020F0;} -.layui-bg-brown{background-color:#A52A2A;} -.layui-bg-violet{background-color:#EE82EE;} -.layui-bg-plum{background-color:#DDA0DD;} -.layui-bg-green{background-color:#67c23a!important;} -.layui-bg-info{background-color:#909399!important;} -.layui-bg-1{ background-color: #fff !important;color:#333} -.layui-bg-2{color: #fff;background-color: #1e9fff;} -.layui-bg-3{color: #fff;background-color: #E6A23C;} -.layui-bg-4{color: #fff;background-color: #F56C6C;} -.layui-bg-5{color: #fff;background-color: #36b368;} -.layui-bg-6{color: #fff;background-color: #bc8fb8;} -.layui-bg-7{color: #fff;background-color: #909399;} -.layui-bg-8{color: #fff;background-color: #fa6141;} -.layui-bg-9{color: #fff;background-color: #2e2e2e;} -.layui-bg-10{color: #fff;background-color: #ec407a;} -.layui-bg-11{color: #fff;background-color: #4285f4;} -.layui-bg-12{color: #fff;background-color: #59698d;} -.layui-bg-13{color: #fff;background-color: #33b5e5;} -.layui-bg-14{color: #fff;background-color: #ff3547;} -.layui-bg-15{color: #fff;background-color: #fb3;} -.layui-bg-16{color: #fff;background-color: #00bcd4 ;} -.layui-bg-17{color: #fff;background-color: #23272b;} -.layui-bg-18{color: #fff;background-color: #07c160;} -.layui-bg-19{color: #fff;background-color: #1989fa;} -.layui-bg-20{color: #fff;background-color: #ee0a24;} -.layui-bg-21{color: #fff;background-color: #ff976a;} -.layui-font-blue{color:#4d70ff!important;} -input[hidden] { height:0!important} -.layui-btn-normal,.layui-bg-blue{background-color:#4d70ff!important;} -.layui-layer-btn .layui-layer-btn0{ border-color: #4d70ff;background-color: #4d70ff} -.layui-laydate .layui-this, .layui-laydate .layui-this>div{background-color: #4d70ff!important;} -.layui-input:focus, .layui-textarea:focus{ border-color: #4d70ff!important;} -.layui-form-required-hidden{opacity: 0;position: absolute;height: 0;width: 0;border: 0;} -.layui-form-danger+.layui-form-select .layui-input, .layui-form-danger:focus{border-color: #ff5722!important;box-shadow: 0 0 0 3px rgba(255,87,34,.08);} \ No newline at end of file +/* ============================================ + Global 全局样式 + 使用 CSS 变量统一管理颜色 + ============================================ */ + +/* 使用 comm.css 中定义的主题变量 */ +.layui-form-radio:hover *, +.layui-form-radioed, +.layui-form-radioed>i { + color: var(--theme-primary, #4d70ff); +} + +.layui-form-onswitch, +.layui-laypage .layui-laypage-curr .layui-laypage-em { + background-color: var(--theme-primary, #4d70ff); +} + +.layui-elem-quote { + border-left: 5px solid var(--theme-primary, #4d70ff); +} + +/* 背景色工具类 */ +.layui-bg-yellow { background-color: #E6A23C; } +.layui-bg-pink { background-color: #FF69B4; } +.layui-bg-purple { background-color: #A020F0; } +.layui-bg-brown { background-color: #A52A2A; } +.layui-bg-violet { background-color: #EE82EE; } +.layui-bg-plum { background-color: #DDA0DD; } +.layui-bg-green { background-color: #67c23a; } +.layui-bg-info { background-color: #909399; } + +/* 数字背景色工具类 */ +.layui-bg-1 { background-color: #fff; color: #333; } +.layui-bg-2 { color: #fff; background-color: #1e9fff; } +.layui-bg-3 { color: #fff; background-color: #E6A23C; } +.layui-bg-4 { color: #fff; background-color: #F56C6C; } +.layui-bg-5 { color: #fff; background-color: #36b368; } +.layui-bg-6 { color: #fff; background-color: #bc8fb8; } +.layui-bg-7 { color: #fff; background-color: #909399; } +.layui-bg-8 { color: #fff; background-color: #fa6141; } +.layui-bg-9 { color: #fff; background-color: #2e2e2e; } +.layui-bg-10 { color: #fff; background-color: #ec407a; } +.layui-bg-11 { color: #fff; background-color: #4285f4; } +.layui-bg-12 { color: #fff; background-color: #59698d; } +.layui-bg-13 { color: #fff; background-color: #33b5e5; } +.layui-bg-14 { color: #fff; background-color: #ff3547; } +.layui-bg-15 { color: #fff; background-color: #fb3; } +.layui-bg-16 { color: #fff; background-color: #00bcd4; } +.layui-bg-17 { color: #fff; background-color: #23272b; } +.layui-bg-18 { color: #fff; background-color: #07c160; } +.layui-bg-19 { color: #fff; background-color: #1989fa; } +.layui-bg-20 { color: #fff; background-color: #ee0a24; } +.layui-bg-21 { color: #fff; background-color: #ff976a; } + +.layui-font-blue { + color: var(--theme-primary, #4d70ff); +} + +input[hidden] { + height: 0; +} + +.layui-btn-normal, +.layui-bg-blue { + background-color: var(--theme-primary, #4d70ff); +} + +.layui-layer-btn .layui-layer-btn0 { + border-color: var(--theme-primary, #4d70ff); + background-color: var(--theme-primary, #4d70ff); +} + +.layui-laydate .layui-this, +.layui-laydate .layui-this>div { + background-color: var(--theme-primary, #4d70ff); +} + +.layui-input:focus, +.layui-textarea:focus { + border-color: var(--theme-primary, #4d70ff); +} + +.layui-form-required-hidden { + opacity: 0; + position: absolute; + height: 0; + width: 0; + border: 0; +} + +.layui-form-danger+.layui-form-select .layui-input, +.layui-form-danger:focus { + border-color: #ff5722; + box-shadow: 0 0 0 3px rgba(255, 87, 34, 0.08); +} diff --git a/public/static/backend/css/login.css b/public/static/backend/css/login.css index d71ed9d65..5fd47c1e5 100644 --- a/public/static/backend/css/login.css +++ b/public/static/backend/css/login.css @@ -1,49 +1,227 @@ -body,html{height:100%} -body{background-color:#F6F6F6;;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;width:100%;overflow:hidden;color:#999;background-size:cover;background-position-x:initial;background-position-y:initial;background-attachment:initial;background-origin:initial;background-clip:initial;} -body:after{content:'';background-repeat:no-repeat;background-size:cover;-webkit-filter:blur(3px);-moz-filter:blur(3px);-o-filter:blur(3px);-ms-filter:blur(3px);filter:blur(3px);position:absolute;top:0;left:0;right:0;bottom:0;z-index:-1} -.layui-row{padding:20px;background: #fff;border-radius:10px;} -.layui-main{max-width: 960px;height: 500px; position: absolute;top: 0;bottom: 0;left: 0;right: 0;margin: auto;} -.flex{display:flex;justify-content: center} -.layui-login-body,.layui-panel-body{background-color:#fff;background-image: url(/static/backend/images/login-picture.svg);background-repeat: no-repeat;background-position: center 60px;background-size: contain; } -.layui-panel-body .layui-logo{ font-size: 28px;margin-left: 15px;color:#4d70ff;vertical-align: middle} -.layui-panel-body .layui-slogan{font-size: 14px;line-height: 20px;text-align: right;color: #333} -.layui-form-item input,.layui-form-item label{transition:all .3s ease-in-out;-webkit-transition:all .3s ease-in-out} -.layui-input{height:45px;} -.layui-container{width:100%;height:100%;overflow:hidden} -.layui-card-header{font-family:'layui-icon';letter-spacing:2px;font-weight:400;font-size:1.875rem;border-bottom:none} -.layui-card-body{background:#fff;line-height:normal;border:none;} -.layui-card-body .layui-form-item{position:relative;margin-bottom:20px; } -.layui-card-body .layui-input-wrap{height:45px;line-height:45px;} -.layui-card-body .layui-form-item .layui-input-prefix i{color: #4d70ff !important} -.layui-form-checked span, .layui-form-checked:hover span{background-color: #4d70ff !important} -.layui-form-checked i, .layui-form-checked:hover i{color: #4d70ff} -.layui-form-checked:hover>div, .layui-form-checked>div{background-color:#4d70ff} -.layui-form-item .captcha-img img{height:40px;vertical-align:middle;} -.layui-form-item.copyright{font-size:12px;color:#999;text-align:center} -.layui-form-item.copyright a{color:#999} -/*.layui-form-checked[lay-skin=primary] i{border-color: #e699c6!important;}*/ -input::-webkit-input-placeholder{color: #bdcada -} -input:-moz-placeholder{color:#bdcada} -input::-moz-placeholder{color: #bdcada;} -input:-ms-input-placeholder{color:#bdcada} - - -@media screen and (min-width:4200px){.layui-main{max-width:1200px} -} -@media screen and (max-width:1200px){.layui-main{max-width:960px} -} -@media screen and (max-width:1024px){.layui-main{max-width:960px} -} -@media screen and (max-width:992px){.layui-main{max-width:800px} -} -@media screen and (max-width:768px){.layui-main{max-width:600px} -} -@media screen and (max-width:540px){.layui-main{max-width:420px} -} -@media screen and (max-width:420px){.layui-main{max-width:360px} -} -@media screen and (max-width:360px){.layui-main{max-width:320px; } -} -@media screen and (max-width:320px){.layui-main{max-width:300px;} -} \ No newline at end of file +/* ============================================ + Login 登录页面样式 + ============================================ */ + +body, +html { + height: 100%; +} + +body { + background-color: #F6F6F6; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + width: 100%; + overflow: hidden; + color: #999; + background-size: cover; + background-position-x: initial; + background-position-y: initial; + background-attachment: initial; + background-origin: initial; + background-clip: initial; +} + +body:after { + content: ''; + background-repeat: no-repeat; + background-size: cover; + filter: blur(3px); + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: -1; +} + +.layui-row { + padding: 20px; + background: #fff; + border-radius: 10px; +} + +.layui-main { + max-width: 960px; + height: 500px; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + margin: auto; +} + +.flex { + display: flex; + justify-content: center; +} + +.layui-login-body, +.layui-panel-body { + background-color: #fff; + background-image: url(/static/backend/images/login-picture.svg); + background-repeat: no-repeat; + background-position: center 60px; + background-size: contain; +} + +.layui-panel-body .layui-logo { + font-size: 28px; + margin-left: 15px; + color: var(--theme-primary, #4d70ff); + vertical-align: middle; +} + +.layui-panel-body .layui-slogan { + font-size: 14px; + line-height: 20px; + text-align: right; + color: #333; +} + +.layui-form-item input, +.layui-form-item label { + transition: all .3s ease-in-out; +} + +.layui-input { + height: 45px; +} + +.layui-container { + width: 100%; + height: 100%; + overflow: hidden; +} + +.layui-card-header { + font-family: 'layui-icon'; + letter-spacing: 2px; + font-weight: 400; + font-size: 1.875rem; + border-bottom: none; +} + +.layui-card-body { + background: #fff; + line-height: normal; + border: none; +} + +.layui-card-body .layui-form-item { + position: relative; + margin-bottom: 20px; +} + +.layui-card-body .layui-input-wrap { + height: 45px; + line-height: 45px; +} + +.layui-card-body .layui-form-item .layui-input-prefix i { + color: var(--theme-primary, #4d70ff); +} + +.layui-form-checked span, +.layui-form-checked:hover span { + background-color: var(--theme-primary, #4d70ff); +} + +.layui-form-checked i, +.layui-form-checked:hover i { + color: var(--theme-primary, #4d70ff); +} + +.layui-form-checked:hover>div, +.layui-form-checked>div { + background-color: var(--theme-primary, #4d70ff); +} + +.layui-form-item .captcha-img img { + height: 40px; + vertical-align: middle; +} + +.layui-form-item.copyright { + font-size: 12px; + color: #999; + text-align: center; +} + +.layui-form-item.copyright a { + color: #999; +} + +input::-webkit-input-placeholder { + color: #bdcada; +} + +input:-moz-placeholder { + color: #bdcada; +} + +input::-moz-placeholder { + color: #bdcada; +} + +input:-ms-input-placeholder { + color: #bdcada; +} + +/* 响应式布局 */ +@media screen and (min-width: 4200px) { + .layui-main { + max-width: 1200px; + } +} + +@media screen and (max-width: 1200px) { + .layui-main { + max-width: 960px; + } +} + +@media screen and (max-width: 1024px) { + .layui-main { + max-width: 960px; + } +} + +@media screen and (max-width: 992px) { + .layui-main { + max-width: 800px; + } +} + +@media screen and (max-width: 768px) { + .layui-main { + max-width: 600px; + } +} + +@media screen and (max-width: 540px) { + .layui-main { + max-width: 420px; + } +} + +@media screen and (max-width: 420px) { + .layui-main { + max-width: 360px; + } +} + +@media screen and (max-width: 360px) { + .layui-main { + max-width: 320px; + } +} + +@media screen and (max-width: 320px) { + .layui-main { + max-width: 300px; + } +} diff --git a/public/static/backend/css/style.css b/public/static/backend/css/style.css index c5654d02a..05cfb3b63 100644 --- a/public/static/backend/css/style.css +++ b/public/static/backend/css/style.css @@ -1,185 +1,148 @@ -/*index页面*/ -body .layui-form-onswitch{border:none;} -#theme0.active .layui-btn, -#theme0.active .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme0.active .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme0.active .layui-form-onswitch, -#theme0.active .fun-layer-class .layui-layer-title {background:#4d70ff !important;color: #fff;} -#theme0.active blockquote{border-left-color:#4d70ff ;} -#theme0.active .layui-form-radio:hover *,#theme0.active .layui-form-radioed,#theme0.active .layui-form-radioed>i{color: #4d70ff } - -#theme1.active .layui-btn, -#theme1.active .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme1.active .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme1.active .layui-form-onswitch, -#theme1.active .fun-layer-class .layui-layer-title {background:#20b598 !important;color: #fff;} -#theme1.active blockquote{border-left-color:#20b598 ;} -#theme1.active .layui-form-radio:hover *, #theme1.active .layui-form-radioed,#theme1.active .layui-form-radioed>i{color: #20b598} - -#theme2.active .layui-btn, -#theme2.active .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme2.active .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme2.active .layui-form-onswitch, -#theme2.active .fun-layer-class .layui-layer-title {background:#8e10b9 !important;color: #fff;} -#theme2.active blockquote{border-left-color:#8e10b9 ;} -#theme2.active .layui-form-radio:hover *,#theme2.active .layui-form-radioed,#theme2.active .layui-form-radioed>i{color: #8e10b9} - -#theme3.active .layui-btn, -#theme3.active .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme3.active .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme3.active .layui-form-onswitch, -#theme3.active .fun-layer-class .layui-layer-title {background:#b8279f !important;color: #fff;} -#theme3.active blockquote{border-left-color:#b8279f ;} -#theme3.active .layui-form-radio:hover *,#theme3.active .layui-form-radioed,#theme3.active .layui-form-radioed>i{color: #b8279f} - -#theme4.active .layui-btn, -#theme4.active .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme4.active .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme4.active .layui-form-onswitch, -#theme4.active .fun-layer-class .layui-layer-title {background:#16b777 !important;color: #fff;} -#theme4.active blockquote{border-left-color:#16b777 ;} -#theme4.active .layui-form-radio:hover *,#theme4.active .layui-form-radioed,#theme4.active .layui-form-radioed>i{color: #16b777} - -#theme5.active .layui-btn, -#theme5.active .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme5.active .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme5.active .layui-form-onswitch, -#theme5.active .fun-layer-class .layui-layer-title {background:#8e10b9 !important;color: #fff;} -#theme5.active blockquote{border-left-color:#8e10b9 ;} -#theme5.active .layui-form-radio:hover *,#theme5.active .layui-form-radioed,#theme5.active .layui-form-radioed>i{color: #8e10b9} - -#theme6.active .layui-btn, -#theme6.active .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme6.active .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme6.active .layui-form-onswitch, -#theme6.active .fun-layer-class .layui-layer-title {background:#20b598 !important;color: #fff;} #theme6.active blockquote{border-left-color:#20b598 ;} -#theme6 .layui-header .layui-layout-center li.layui-this{background: #009688;} -#theme6.active .layui-form-radio:hover *,#theme6.active .layui-form-radioed,#theme6.active .layui-form-radioed>i{color: #20b598} - -#theme7.active .layui-btn, -#theme7.active .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme7.active .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme7.active .layui-form-onswitch, -#theme7.active .fun-layer-class .layui-layer-title {background:#20b598 !important;color: #fff;} #theme7.active blockquote{border-left-color:#20b598 ;} -#theme7 .layui-header .layui-layout-center li.layui-this{background: #009688;} -#theme7.active .layui-form-radio:hover *,#theme7.active .layui-form-radioed,#theme7.active .layui-form-radioed>i{color: #009688} - - -#theme8.active .layui-btn, -#theme8.active .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme8.active .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme8.active .layui-form-onswitch, -#theme8.active .fun-layer-class .layui-layer-title {background:#00a65a !important;color: #fff;} #theme8.active blockquote{border-left-color:#00a65a ;} -#theme8 .layui-header .layui-layout-center li.layui-this{background: #5fb878;} -#theme8.active .layui-form-radio:hover *,#theme8.active .layui-form-radioed,#theme8.active .layui-form-radioed>i{color: #00a65a} - -#theme9.active .layui-btn, -#theme9.active .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme9.active .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme9.active .layui-form-onswitch, -#theme9.active .fun-layer-class .layui-layer-title {background:#8e10b9 !important;color: #fff;} #theme9.active blockquote{border-left-color:#8e10b9 ;} -#theme9 .layui-header .layui-layout-center li.layui-this{background: #4f179e;} -#theme9.active .layui-form-radio:hover *,#theme9.active .layui-form-radioed,#theme9.active .layui-form-radioed>i{color: #8e10b9} - -#theme10.active .layui-btn, -#theme10.active .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme10.active .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme10.active .layui-form-onswitch, -#theme10.active .fun-layer-class .layui-layer-title {background:#00a65a !important;color: #fff;} #theme10.active blockquote{border-left-color:#00a65a ;} -#theme10 .layui-header .layui-layout-center li.layui-this{background: #009688;} -#theme10.active .layui-form-radio:hover *,#theme10.active .layui-form-radioed,#theme10.active .layui-form-radioed>i{color: #00a65a} - -#theme11.active .layui-btn, -#theme11.active .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme11.active .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme11.active .layui-form-onswitch, -#theme11.active .fun-layer-class .layui-layer-title {background:#ffa4d1 !important;color: #fff;} #theme11.active blockquote{border-left-color:#ffa4d1 ;} -#theme11 .layui-header .layui-layout-center li.layui-this{background: #dc6fa5;} -#theme11.active .layui-form-radio:hover *,#theme11.active .layui-form-radioed,#theme11.active .layui-form-radioed>i{color: #ffa4d1} - -#theme12.active .layui-btn, -#theme12.active .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme12.active .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme12.active .layui-form-onswitch, -#theme12.active .fun-layer-class .layui-layer-title {background:#1e9fff !important;color: #fff;} #theme12.active blockquote{border-left-color:#1e9fff ;} -#theme12 .layui-header .layui-layout-center li.layui-this{background: #0d73bf;} -#theme12.active .layui-form-radio:hover *,#theme12.active .layui-form-radioed,#theme12.active .layui-form-radioed>i{color: #1e9fff} - -#theme13.active .layui-btn, -#theme13.active .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme13.active .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme13.active .layui-form-onswitch, -#theme13.active .fun-layer-class .layui-layer-title {background:#963885 !important;color: #fff;} #theme13.active blockquote{border-left-color:#963885 ;} -#theme13 .layui-header .layui-layout-center li.layui-this{background: #b8279f;} -#theme13.active .layui-form-radio:hover *,#theme13.active .layui-form-radioed,#theme13.active .layui-form-radioed>i{color: #963885} - -#theme14.active .layui-btn, -#theme14.active .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme14.active .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme14.active .layui-form-onswitch, -#theme14.active .fun-layer-class .layui-layer-title {background:#963885 !important;color: #fff;} #theme14.active blockquote{border-left-color:#963885 ;} -#theme14 .layui-header .layui-layout-center li.layui-this{background: #b8279f;} -#theme14.active .layui-form-radio:hover *,#theme14.active .layui-form-radioed,#theme14.active .layui-form-radioed>i{color: #963885} - -#theme15.active .layui-btn, -#theme15.active .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme15.active .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme15.active .layui-form-onswitch, -#theme15.active .fun-layer-class .layui-layer-title {background:#1e9fff !important;color: #fff;} #theme15.active blockquote{border-left-color:#1e9fff ;} -#theme15 .layui-header .layui-layout-center li.layui-this{background: #1877bd;} -#theme15.active .layui-form-radio:hover *,#theme15.active .layui-form-radioed,#theme15.active .layui-form-radioed>i{color: #1e9fff} - -#theme16.active .layui-btn, -#theme16.active .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme16.active .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme16.active .layui-form-onswitch, -#theme16.active .fun-layer-class .layui-layer-title {background:#ffb800 !important;color: #fff;} #theme16.active blockquote{border-left-color:#ffb800 ;} -#theme16 .layui-header .layui-layout-center li.layui-this{background: #c39316;} -#theme16.active .layui-form-radio:hover *,#theme16.active .layui-form-radioed,#theme16.active .layui-form-radioed>i{color: #ffb800} - -#theme17.active .layui-btn, -#theme17.active .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme17.active .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme17.active .layui-form-onswitch, -#theme17.active .fun-layer-class .layui-layer-title {background:#ffb800 !important;color: #fff;} -#theme17.active blockquote{border-left-color:#ffb800 ;} -#theme17 .layui-header .layui-layout-center li.layui-this{background: #b7880d;} -#theme17.active .layui-form-radio:hover *,#theme17.active .layui-form-radioed,#theme17.active .layui-form-radioed>i{color: #ffb800} - -#theme18.active .layui-btn, -#theme18.active .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme18.active .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme18.active .layui-form-onswitch, -#theme18.active .fun-layer-class .layui-layer-title {background:#e82121 !important;color: #fff;} -#theme18.active blockquote{border-left-color:#e82121 ;} -#theme18 .layui-header .layui-layout-center li.layui-this{background: #a22222;} -#theme18.active .layui-form-radio:hover *,#theme18.active .layui-form-radioed,#theme18.active .layui-form-radioed>i{color: #e82121} - -#theme19.active .layui-btn, -#theme19.active .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme19.active .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme19.active .layui-form-onswitch, -#theme19.active .fun-layer-class .layui-layer-title {background:#e82121 !important;color: #fff;} -#theme19.active blockquote{border-left-color:#e82121 ;} -#theme19 .layui-header .layui-layout-center li.layui-this{background: #a22222;} -#theme19.active .layui-form-radio:hover *,#theme19.active .layui-form-radioed,#theme19.active .layui-form-radioed>i{color: #e82121} - -#theme20.active .layui-btn, -#theme20.active .fun-layer-class .layui-layer-btn .layui-layer-btn0, -#theme20.active .layui-laypage .layui-laypage-curr .layui-laypage-em, -#theme20.active .layui-form-onswitch, -#theme20.active .fun-layer-class .layui-layer-title {background:#4d70ff !important;color: #fff;} -#theme20.active blockquote{border-left-color:#4d70ff;} -#theme20.active .layui-form-radio:hover *,#theme20.active .layui-form-radioed,#theme20.active .layui-form-radioed>i{color: #4d70ff} - -#theme20 .layui-nav{color: #333;} -#theme20 .layui-header .layui-layout-center li.layui-this{background: #e2ebfb;} -#theme20 .layui-layout-admin .layui-side-scroll .layui-nav-tree .layui-nav-item>a {color:#333;} -#theme20 .layui-layout-admin .layui-side-scroll .layui-nav-tree .layui-nav-item>a:hover{color: #333!important;} -#theme20 .layui-nav-itemed>a, .layui-nav-tree .layui-nav-title a, .layui-nav-tree .layui-nav-title a:hover{color: #333!important;} -#theme20 .layui-layout-admin .layui-side-menu .layui-nav .layui-nav-itemed>.layui-nav-child {background-color: #fff!important;} -#theme20 .layui-pagetabs .layui-tab-title li:hover,#theme20 .layui-pagetabs .layui-tab-title li.layui-this{color:#4d70ff!important} -@media screen and (min-width:769px) { -#theme20 .layui-side-shrink .layui-side-menu .layui-nav-item.layui-nav-hover cite{color:#4d70ff!important} +/* ============================================ + Index 页面主题样式 + 使用 CSS 变量统一管理 + 注意:主题变量定义在 comm.css 中,这里只定义激活状态的映射 + ============================================ */ + +body .layui-form-onswitch { + border: none; } +/* 主题激活状态变量映射 - 引用 comm.css 中定义的主题变量 */ +[id^="theme"].active { + --theme-color: var(--theme-0); +} + +#theme0.active { --theme-color: var(--theme-0); } +#theme1.active { --theme-color: var(--theme-1); } +#theme2.active { --theme-color: var(--theme-2); } +#theme3.active { --theme-color: var(--theme-3); } +#theme4.active { --theme-color: var(--theme-4); } +#theme5.active { --theme-color: var(--theme-5); } +#theme6.active { --theme-color: var(--theme-6); } +#theme7.active { --theme-color: var(--theme-7); } +#theme8.active { --theme-color: var(--theme-8); } +#theme9.active { --theme-color: var(--theme-9); } +#theme10.active { --theme-color: var(--theme-10); } +#theme11.active { --theme-color: var(--theme-11); } +#theme12.active { --theme-color: var(--theme-12); } +#theme13.active { --theme-color: var(--theme-13); } +#theme14.active { --theme-color: var(--theme-14); } +#theme15.active { --theme-color: var(--theme-15); } +#theme16.active { --theme-color: var(--theme-16); } +#theme17.active { --theme-color: var(--theme-17); } +#theme18.active { --theme-color: var(--theme-18); } +#theme19.active { --theme-color: var(--theme-19); } +#theme20.active { --theme-color: var(--theme-20); } + +/* 统一主题激活状态样式 */ +[id^="theme"].active .layui-btn, +[id^="theme"].active .fun-layer-class .layui-layer-btn .layui-layer-btn0, +[id^="theme"].active .layui-laypage .layui-laypage-curr .layui-laypage-em, +[id^="theme"].active .layui-form-onswitch, +[id^="theme"].active .fun-layer-class .layui-layer-title { + background: var(--theme-color); + color: #fff; +} + +[id^="theme"].active blockquote { + border-left-color: var(--theme-color); +} + +[id^="theme"].active .layui-form-radio:hover *, +[id^="theme"].active .layui-form-radioed, +[id^="theme"].active .layui-form-radioed>i { + color: var(--theme-color); +} + +/* 特殊主题样式 */ +#theme6 .layui-header .layui-layout-center li.layui-this, +#theme7 .layui-header .layui-layout-center li.layui-this { + background: #009688; +} + +#theme7.active .layui-form-radio:hover *, +#theme7.active .layui-form-radioed, +#theme7.active .layui-form-radioed>i { + color: #009688; +} + +#theme8 .layui-header .layui-layout-center li.layui-this { + background: #5fb878; +} + +#theme9 .layui-header .layui-layout-center li.layui-this { + background: #4f179e; +} + +#theme10 .layui-header .layui-layout-center li.layui-this { + background: #009688; +} + +#theme11 .layui-header .layui-layout-center li.layui-this { + background: #dc6fa5; +} + +#theme12 .layui-header .layui-layout-center li.layui-this { + background: #0d73bf; +} + +#theme13 .layui-header .layui-layout-center li.layui-this, +#theme14 .layui-header .layui-layout-center li.layui-this { + background: #b8279f; +} + +#theme15 .layui-header .layui-layout-center li.layui-this { + background: #1877bd; +} + +#theme16 .layui-header .layui-layout-center li.layui-this { + background: #c39316; +} + +#theme17 .layui-header .layui-layout-center li.layui-this { + background: #b7880d; +} + +#theme18 .layui-header .layui-layout-center li.layui-this, +#theme19 .layui-header .layui-layout-center li.layui-this { + background: #a22222; +} + +/* Theme20 特殊样式 */ +#theme20 .layui-nav { + color: #333; +} + +#theme20 .layui-header .layui-layout-center li.layui-this { + background: #e2ebfb; +} + +#theme20 .layui-layout-admin .layui-side-scroll .layui-nav-tree .layui-nav-item>a { + color: #333; +} + +#theme20 .layui-layout-admin .layui-side-scroll .layui-nav-tree .layui-nav-item>a:hover { + color: #333; +} + +#theme20 .layui-nav-itemed>a, +#theme20 .layui-nav-tree .layui-nav-title a, +#theme20 .layui-nav-tree .layui-nav-title a:hover { + color: #333; +} + +#theme20 .layui-layout-admin .layui-side-menu .layui-nav .layui-nav-itemed>.layui-nav-child { + background-color: #fff; +} + +#theme20 .layui-pagetabs .layui-tabs-header li:hover, +#theme20 .layui-pagetabs .layui-tabs-header li.layui-this { + color: #4d70ff; +} + +@media screen and (min-width: 769px) { + #theme20 .layui-side-shrink .layui-side-menu .layui-nav-item.layui-nav-hover cite { + color: #4d70ff; + } +} diff --git a/public/static/backend/css/theme0.css b/public/static/backend/css/theme0.css index e20ad4ce5..4031c2fd9 100644 --- a/public/static/backend/css/theme0.css +++ b/public/static/backend/css/theme0.css @@ -1,45 +1,45 @@ .layui-layout-admin .layui-header{display: flex;background: #fff!important;} .layui-layout-admin .layui-pagetabs{top: 0;position: absolute;left: 220px;} -.layui-layout-admin .layui-pagetabs .layui-tab-content{top: 60px; position: fixed;left: 220px;} -.layui-layout-admin .layui-pagetabs .layui-tab-title li.layui-this:after{background:rgba(0, 0, 0, 0.02);} -.layui-layout-admin .layui-pagetabs .layui-tab{width: 100%;margin: 0 auto;left: 0!important;} -.layui-layout-admin .layui-pagetabs .layui-tab .layui-tab-title{height:60px;width: 100%;} -.layui-layout-admin .layui-pagetabs .layui-tab .layui-tab-title .layui-this:after{height: 60px;} -.layui-layout-admin .layui-pagetabs .layui-tab ul{color: #000;} -.layui-layout-admin .layui-pagetabs .layui-tab ul .layui-tab-bar{display: none;} -.layui-layout-admin .layui-pagetabs .layui-tab .layui-tab-title li{line-height: 60px;} -.layui-layout-admin .layui-pagetabs .layui-tab-title li:first-child .layui-tab-close{display: none;} -.layui-layout-admin .layui-pagetabs .layui-tab li .layui-tab-close{display: inline;} -.layui-layout-admin .layui-pagetabs .layui-tab li cite{font-style:normal;} -.layui-layout-admin .layui-tab-title{left: 54px;} -.layui-layout-admin .layui-pagetabs .layui-tab-title li:first-child{min-width: 30px;} -.layui-layout-admin .layui-pagetabs .layui-tab-title li:not(:first-child){min-width: 70px} -.layui-layout-admin .layui-header ul li a{color: #000!important;} +.layui-layout-admin .layui-pagetabs .layui-tabs-body{top: 60px; position: fixed;left: 220px;} +.layui-layout-admin .layui-pagetabs .layui-tabs-header li.layui-this:after{background:rgba(0, 0, 0, 0.02);} +.layui-layout-admin .layui-pagetabs .layui-tabs{width: 100%;margin: 0 auto;left: 0!important;} +.layui-layout-admin .layui-pagetabs .layui-tabs .layui-tabs-header{height:60px;width: 100%;} +.layui-layout-admin .layui-pagetabs .layui-tabs .layui-tabs-header .layui-this:after{height: 60px;} +.layui-layout-admin .layui-pagetabs .layui-tabs ul{color: #000;} +.layui-layout-admin .layui-pagetabs .layui-tabs .layui-tabs-header li{line-height: 60px;} +.layui-layout-admin .layui-pagetabs .layui-tabs .layui-tabs-header li.console{width: 60px;background-color: #fff} +.layui-layout-admin .layui-pagetabs .layui-tabs .layui-tabs-scroll{height: 60px;line-height: 60px;} +.layui-layout-admin .layui-pagetabs .layui-tabs .layui-tabs-scroll .layui-tabs-bar .layui-icon{height: 60px;line-height: 60px; + width: 60px;color: #333} +.layui-layout-admin .layui-tabs-header{left: 54px;} +.layui-layout-admin .layui-pagetabs .layui-tabs-header li:first-child{min-width: 30px;} +.layui-layout-admin .layui-pagetabs .layui-tabs-header li:not(:first-child){min-width: 70px} +.layui-layout-admin .layui-header ul li>a{color: #000!important;} .layui-layout-admin .layui-header .layui-layout-center li.layui-this {background: #e2ebfb!important;color:#333!important;} .layui-layout-admin .layui-header .layui-layout-left{background: #fff!important;color:#000!important;border-right:1px solid #eee;left:0} .layui-layout-admin .layui-header .layui-layout-right{background: #fff!important;color:#000!important;} .layui-layout-admin .layui-body{bottom: 0;margin-bottom: 30px!important;} -.layui-body.layui-tab-content .layui-tab-item.layui-show{height: 100% !important} +.layui-body.layui-tabs-body .layui-tabs-item.layui-show{height: 100% !important} @media screen and (min-width: 1024px) { - .layui-layout-admin .layui-pagetabs .layui-tab{left:274px;} - .layui-layout-admin .layui-pagetabs .layui-tab-title{left:54px;} + .layui-layout-admin .layui-pagetabs .layui-tabs{left:274px;} + .layui-layout-admin .layui-pagetabs .layui-tabs-header{left:54px;} .layui-side-shrink .layui-pagetabs{left: 60px;} - .layui-side-shrink .layui-pagetabs .layui-tab-content{left: 60px;} + .layui-side-shrink .layui-pagetabs .layui-tabs-body{left: 60px;} } @media screen and (min-width: 769px) { - .layui-layout-admin .layui-pagetabs .layui-tab-content {left: 220px;} - .layui-side-shrink .layui-pagetabs .layui-tab-content {left: 60px;} + .layui-layout-admin .layui-pagetabs .layui-tabs-body {left: 220px;} + .layui-side-shrink .layui-pagetabs .layui-tabs-body {left: 60px;} } @media screen and (max-width: 1024px) { .layui-side-shrink .layui-pagetabs{left: 0!important;} - .layui-side-shrink .layui-pagetabs .layui-tab-content{left: 0;} - .layui-layout-admin .layui-pagetabs .layui-tab{left:60px;} + .layui-side-shrink .layui-pagetabs .layui-tabs-body{left: 0;} + .layui-layout-admin .layui-pagetabs .layui-tabs{left:60px;} } @media screen and (max-width: 769px){ .layui-layout-admin .layui-header{height: 100%;} .layui-layout-admin .layui-pagetabs { left: 0;} - .layui-layout-admin .layui-pagetabs .layui-tab-content{left: 0;} + .layui-layout-admin .layui-pagetabs .layui-tabs-body{left: 0;} .layui-side-shrink .layui-pagetabs .layui-layout-center{left: 0!important;} .layui-side-shrink .layui-layout-admin .layui-pagetabs{transform: translate3d(220px, 0, 0); -webkit-transform: translate3d(220px, 0, 0); } .layui-side-shrink .layui-layout-admin .layui-layout-left{transform: translate3d(0px, 0, 0);-webkit-transform: translate3d(0px, 0, 0);} diff --git a/public/static/backend/css/theme2.css b/public/static/backend/css/theme2.css index de592bc16..e1c991c73 100644 --- a/public/static/backend/css/theme2.css +++ b/public/static/backend/css/theme2.css @@ -21,7 +21,7 @@ position: fixed; top:60px;width:100%; z-index:999; } -.layui-layout-admin .layui-tab-header{ +.layui-layout-admin .layui-tabs-header{ display: none!important; } .layui-layout-admin .layui-body{ diff --git a/public/static/backend/css/theme4.css b/public/static/backend/css/theme4.css index 226b50a5e..fb2ce03a9 100644 --- a/public/static/backend/css/theme4.css +++ b/public/static/backend/css/theme4.css @@ -85,7 +85,7 @@ position: fixed; top:60px;width:100%; z-index:999; } -.layui-layout-admin .layui-tab-header{ +.layui-layout-admin .layui-tabs-header{ display: none!important; } .layui-layout-admin .layui-body{ diff --git a/public/static/backend/css/theme5.css b/public/static/backend/css/theme5.css index 7e1dbdf28..94fbd6253 100644 --- a/public/static/backend/css/theme5.css +++ b/public/static/backend/css/theme5.css @@ -1,6 +1,3 @@ -#layui-app-body{ - /*top:0;*/ -} .layui-layout-admin .layui-pagetabs{ left: 0;top: 60px!important;; } @@ -25,7 +22,7 @@ position: fixed; top:60px;width:100%; z-index:999; } -.layui-layout-admin .layui-tab-header{ +.layui-layout-admin .layui-tabs-header{ display: none!important; } .layui-layout-admin .layui-body{ diff --git a/public/static/backend/js/addon.js b/public/static/backend/js/addon.js index 9fbbeccf4..3ccb2c52e 100644 --- a/public/static/backend/js/addon.js +++ b/public/static/backend/js/addon.js @@ -6,12 +6,12 @@ define(['table', 'form', 'md5','upload'], function (Table, Form, Md5,Upload) { table_elem: 'list', tableId: 'list', requests: { - index_url: 'addon/index', - install_url: 'addon/install', - uninstall_url: 'addon/uninstall', - config_url: 'addon/config', - modify_url: 'addon/modify', - logout_url: 'addon/logout', + index: 'addon/index', + install: 'addon/install', + uninstall: 'addon/uninstall', + config: 'addon/config', + modify: 'addon/modify', + logout: 'addon/logout', localinstall:{ type: 'upload', class: 'layui-btn-sm layui-btn-danger', @@ -55,7 +55,7 @@ define(['table', 'form', 'md5','upload'], function (Table, Form, Md5,Upload) { Table.render({ elem: '#' + Table.init.table_elem, id: Table.init.table_render_id, - url: Fun.url(Table.init.requests.index_url), + url: Fun.url(Table.init.requests.index), init: Table.init, toolbar: ['refresh','localinstall','plugins','create','account'], searchInput:true, @@ -100,7 +100,7 @@ define(['table', 'form', 'md5','upload'], function (Table, Form, Md5,Upload) { { field: 'version', title: __('Addon version'), width: 100, search: false, templet: function (d) { - return d['pluginsVersion'] ? d['pluginsVersion'] ['0']['version'] : d.version; + return d['pluginsVersion'] ? d['pluginsVersion']['0']['version'] : d.version; } }, // {field: 'requires', title: __('Addon require'), width: 160, sort: true, search: false}, @@ -124,16 +124,16 @@ define(['table', 'form', 'md5','upload'], function (Table, Form, Md5,Upload) { if (d.install && d.install == 1 ) { if(d.lastVersion > d.localVersion){ html += ""; } - html += '' + html += '' if (d.status == 1 ) { - html += '' + html += '' } else { - html += '' + html += '' } - html += '' + html += '' if (d.website !== '') { html += '演示'; } @@ -144,7 +144,7 @@ define(['table', 'form', 'md5','upload'], function (Table, Form, Md5,Upload) { if(d.hasOwnProperty('kinds') && d.kinds==10){ html+="点击了解" }else{ - html += '' + html += '' } } return html; @@ -202,7 +202,7 @@ define(['table', 'form', 'md5','upload'], function (Table, Form, Md5,Upload) { btnAlign: 'c', btn: [__('Login'),__('Register')], yes: function (index, layero) { - var url = Fun.url(Table.init.requests.index_url); + var url = Fun.url(Table.init.requests.index); var data = { username: $("#inputUsername", layero).val(), password: $("#inputPassword", layero).val(), @@ -305,7 +305,7 @@ define(['table', 'form', 'md5','upload'], function (Table, Form, Md5,Upload) { btnAlign: 'c', btn: [__('Login'),__('Register')], yes: function (index, layero) { - var url = Fun.url(Table.init.requests.index_url); + var url = Fun.url(Table.init.requests.index); var data = { username: $("#inputUsername", layero).val(), password: $("#inputPassword", layero).val(), @@ -349,7 +349,7 @@ define(['table', 'form', 'md5','upload'], function (Table, Form, Md5,Upload) { //指定允许上传的文件类型 var uploadinit = layui.upload.render({ elem: '#importFile' - ,url: Fun.url(Upload.init.requests.upload_url)+'?save=1&path=addon' //改成您自己的上传接口 + ,url: Fun.url(Upload.init.requests.upload)+'?save=1&path=addon' //改成您自己的上传接口 ,accept: 'file' //普通文件 ,exts: 'zip|rar|7z' //只允许上传压缩文件 ,size:1024*50 @@ -382,7 +382,6 @@ define(['table', 'form', 'md5','upload'], function (Table, Form, Md5,Upload) { } }); AccountClick = function(e){ - console.log() let funadmin_memberinfo = Fun.api.getStorage('funadmin_memberinfo') if(typeof funadmin_memberinfo !=='undefined' && funadmin_memberinfo!=''){ layer.open({ @@ -396,7 +395,7 @@ define(['table', 'form', 'md5','upload'], function (Table, Form, Md5,Upload) { btnAlign: 'c', btn: [__('Logout'),__('Register')], yes: function (index, layero) { - var url = Fun.url(Table.init.requests.logout_url); + var url = Fun.url(Table.init.requests.logout); $.ajax({ url: url, type: 'post', dataType: "json", success: function (res) { if (res.code === 1) { @@ -434,7 +433,7 @@ define(['table', 'form', 'md5','upload'], function (Table, Form, Md5,Upload) { btnAlign: 'c', btn: [__('Login'),__('Register')], yes: function (index, layero) { - var url = Fun.url(Table.init.requests.index_url); + var url = Fun.url(Table.init.requests.index); var data = { username: $("#inputUsername", layero).val(), password: $("#inputPassword", layero).val(), diff --git a/public/static/backend/js/auth/admin.js b/public/static/backend/js/auth/admin.js index 414df1353..69a5947d4 100644 --- a/public/static/backend/js/auth/admin.js +++ b/public/static/backend/js/auth/admin.js @@ -5,11 +5,11 @@ define(['table', 'form'], function (Table, Form) { table_elem: 'list', tableId: 'list', requests: { - modify_url: 'auth.admin/modify', - index_url: 'auth.admin/index', - delete_url: 'auth.admin/delete', - add_url: 'auth.admin/add', - edit_url: 'auth.admin/edit', + modify: 'auth.admin/modify', + index: 'auth.admin/index', + delete: 'auth.admin/delete', + add: 'auth.admin/add', + edit: 'auth.admin/edit', add_full:{ type: 'open', class: 'layui-btn-sm', @@ -34,7 +34,7 @@ define(['table', 'form'], function (Table, Form) { Table.render({ elem: '#' + Table.init.table_elem, id: Table.init.tableId, - url: Fun.url(Table.init.requests.index_url), + url: Fun.url(Table.init.requests.index), init: Table.init, toolbar: ['refresh', 'add_full', 'delete'], cols: [[ diff --git a/public/static/backend/js/auth/auth.js b/public/static/backend/js/auth/auth.js index 1077c043c..a2637f404 100644 --- a/public/static/backend/js/auth/auth.js +++ b/public/static/backend/js/auth/auth.js @@ -6,9 +6,9 @@ define(['table','form'], function (Table, Form) { table_elem: 'list', tableId: 'list', requests: { - index_url: 'auth.auth/index', - add_url: 'auth.auth/add', - edit_url: 'auth.auth/edit', + index: 'auth.auth/index', + add: 'auth.auth/add', + edit: 'auth.auth/edit', delete: { type: 'request', class: 'layui-btn-xs layui-btn-warm', @@ -20,7 +20,7 @@ define(['table','form'], function (Table, Form) { width:'', height:'', }, - modify_url: 'auth.auth/modify', + modify: 'auth.auth/modify', child: { type: 'open', class: 'layui-btn-xs layui-btn-warm', @@ -64,7 +64,7 @@ define(['table','form'], function (Table, Form) { Table.render({ id: Table.init.tableId, elem: '#' + Table.init.table_elem, - url: Fun.url(Table.init.requests.index_url), + url: Fun.url(Table.init.requests.index), init: Table.init, toolbar:['refresh','add','expand','close'], // maxHeight: '501px', @@ -129,7 +129,7 @@ define(['table','form'], function (Table, Form) { ]] ,page:false }); - var url = Fun.url(Table.init.requests.modify_url); + var url = Fun.url(Table.init.requests.modify); Table.api.bindEvent(Table.init.tableId); form.on('switch(auth_verify)', function (obj) { diff --git a/public/static/backend/js/auth/authgroup.js b/public/static/backend/js/auth/authgroup.js index 190d5b8cf..f596ccf08 100644 --- a/public/static/backend/js/auth/authgroup.js +++ b/public/static/backend/js/auth/authgroup.js @@ -5,11 +5,11 @@ define(['table', 'form'], function (Table, Form) { table_elem: 'list', tableId: 'list', requests: { - modify_url: 'auth.authGroup/modify', - index_url: 'auth.authGroup/index', - delete_url: 'auth.authGroup/delete', - add_url: 'auth.authGroup/add', - edit_url: 'auth.authGroup/edit', + modify: 'auth.authGroup/modify', + index: 'auth.authGroup/index', + delete: 'auth.authGroup/delete', + add: 'auth.authGroup/add', + edit: 'auth.authGroup/edit', access: { type: 'open', class: 'layui-btn-xs layui-btn-warm', @@ -24,7 +24,7 @@ define(['table', 'form'], function (Table, Form) { Table.render({ elem: '#' + Table.init.table_elem, id: Table.init.tableId, - url: Fun.url(Table.init.requests.index_url), + url: Fun.url(Table.init.requests.index), init: Table.init, toolbar: ['refresh', 'add', 'delete'], cols: [[ diff --git a/public/static/backend/js/member/member.js b/public/static/backend/js/member/member.js index fbf8a799d..3bdfe6b20 100644 --- a/public/static/backend/js/member/member.js +++ b/public/static/backend/js/member/member.js @@ -3,17 +3,17 @@ define(['table','form'], function (Table,Form) { table_elem: 'list', tableId: 'list', requests: { - modify_url: 'member.member/modify', - index_url: 'member.member/index', - recycle_url: 'member.member/recycle', - delete_url: 'member.member/delete', - destroy_url: 'member.member/destroy', - export_url: 'member.member/export', - import_url: 'member.member/import', - restore_url: 'member.member/restore', - copy_url: 'member.member/copy', - // add_url: 'member.member/add', - // edit_url: 'member.member/edit', + modify: 'member.member/modify', + index: 'member.member/index', + recycle: 'member.member/recycle', + delete: 'member.member/delete', + destroy: 'member.member/destroy', + export: 'member.member/export', + import: 'member.member/import', + restore: 'member.member/restore', + copy: 'member.member/copy', + // add: 'member.member/add', + // edit: 'member.member/edit', add_full:{ type: 'open', class: 'layui-btn-sm layui-btn-green', @@ -25,7 +25,7 @@ define(['table','form'], function (Table,Form) { // full: 1, // width:'800', }, - edit_url:{ + edit:{ type: 'open', event: 'open', class: 'layui-btn-xs layui-btn-green', @@ -90,17 +90,17 @@ define(['table','form'], function (Table,Form) { table_elem: 'list1', tableId: 'list1', requests: { - modify_url: 'member.memberLevel/modify', - index_url: 'member.memberLevel/index', - recycle_url: 'member.memberLevel/recycle', - delete_url: 'member.memberLevel/delete', - destroy_url: 'member.memberLevel/destroy', - export_url: 'member.memberLevel/export', - import_url: 'member.memberLevel/import', - restore_url: 'member.memberLevel/restore', - copy_url: 'member.memberLevel/copy', - // add_url: 'member.member/add', - // edit_url: 'member.member/edit', + modify: 'member.memberLevel/modify', + index: 'member.memberLevel/index', + recycle: 'member.memberLevel/recycle', + delete: 'member.memberLevel/delete', + destroy: 'member.memberLevel/destroy', + export: 'member.memberLevel/export', + import: 'member.memberLevel/import', + restore: 'member.memberLevel/restore', + copy: 'member.memberLevel/copy', + // add: 'member.member/add', + // edit: 'member.member/edit', add_full:{ type: 'open', class: 'layui-btn-sm layui-btn-green', @@ -112,7 +112,7 @@ define(['table','form'], function (Table,Form) { // full: 1, // width:'800', }, - edit_url:{ + edit:{ type: 'iframe', event: 'iframe', class: 'layui-btn-xs layui-btn-green', @@ -185,7 +185,7 @@ define(['table','form'], function (Table,Form) { elem: '#' + Table.init.table_elem, id: Table.init.tableId, css: '.layui-table-cell{height: 50px; line-height: 40px; overflow: visible;}', - url: Fun.url(Table.init.requests.index_url), + url: Fun.url(Table.init.requests.index), init: Table.init, primaryKey: 'id', // primaryKey:"member_id", @@ -193,7 +193,7 @@ define(['table','form'], function (Table,Form) { searchType:1, // searchFormTpl:'search',//模板ID toolbar: ['refresh','add_full','destroy','import','export','recycle'], - // toolbar: ['refresh','add_full','edit_url','destroy','import','export','recycle'], + // toolbar: ['refresh','add_full','edit','destroy','import','export','recycle'], cols: [[ {checkbox: true,}, {field: 'id', title: 'ID', width: 80, sort: true}, @@ -213,10 +213,11 @@ define(['table','form'], function (Table,Form) { tips: __('Female')+'|'+ __('Male') }, { - field: 'memberLevel.name', + field: 'level.name', title: __('MemberLevel'), width: 120, // url:'member.memberLevel/index', + fieldAlias:'level_id', selectList: memberLevel, prop:'name,name', search: 'select', @@ -241,8 +242,8 @@ define(['table','form'], function (Table,Form) { title: __('Operat'), init: Table.init, templet: Table.templet.operat, - // operat: ['edit_url', 'destroy',], - operat: ['edit_url','copy', 'destroy','dropdown'], + // operat: ['edit', 'destroy',], + operat: ['edit','copy', 'destroy','dropdown'], // operat:function(d){ // return ['edit_s','destroy']; // }, @@ -252,7 +253,7 @@ define(['table','form'], function (Table,Form) { // } // operat:'', // operat:'#demo', - // operat: ['edit_url', 'destroy', + // operat: ['edit', 'destroy', // // { // // type: 'x', // // event: 'x', @@ -283,14 +284,14 @@ define(['table','form'], function (Table,Form) { elem: '#' + Table.init2.table_elem, id: Table.init2.tableId, css: '.layui-table-cell{height: 50px; line-height: 40px; overflow: visible;}', - url: Fun.url(Table.init2.requests.index_url), + url: Fun.url(Table.init2.requests.index), init: Table.init2, primaryKey: 'id', // primaryKey:"member_id", searchShow:true, // searchFormTpl:'search',//模板ID toolbar: ['refresh','add_full','destroy','import','export','recycle'], - // toolbar: ['refresh','add_full','edit_url','destroy','import','export','recycle'], + // toolbar: ['refresh','add_full','edit','destroy','import','export','recycle'], cols: [[ {checkbox: true, }, {field: 'id', title: __('Id'), width: 80, sort: true}, @@ -342,7 +343,7 @@ define(['table','form'], function (Table,Form) { Table.render({ elem: '#' + Table.init.table_elem, id: Table.init.tableId, - url: Fun.url(Table.init.requests.recycle_url), + url: Fun.url(Table.init.requests.recycle), init: Table.init, toolbar: ['refresh','delete','restore'], cols: [[ @@ -362,7 +363,10 @@ define(['table','form'], function (Table,Form) { tips: __('Female')+'|'+ __('Male') }, { - field: 'memberLevel.name', + field: 'level.name', + fieldAlias:'level_id', + selectList: memberLevel, + prop:'name,name', title: __('MemberLevel'), width: 120, templet: Table.templet.text diff --git a/public/static/backend/js/member/membergroup.js b/public/static/backend/js/member/membergroup.js index 8c123a1ea..c08fb5bb2 100644 --- a/public/static/backend/js/member/membergroup.js +++ b/public/static/backend/js/member/membergroup.js @@ -3,14 +3,14 @@ define(['table','form'], function (Table,Form) { table_elem: 'list', tableId: 'list', requests: { - modify_url: 'member.memberGroup/modify', - index_url: 'member.memberGroup/index', - add_url: 'member.memberGroup/add', - delete_url: 'member.memberGroup/delete', - destroy_url: 'member.memberGroup/destroy', - edit_url: 'member.memberGroup/edit', - recycle_url: 'member.memberGroup/recycle', - export_url: 'member.memberGroup/export', + modify: 'member.memberGroup/modify', + index: 'member.memberGroup/index', + add: 'member.memberGroup/add', + delete: 'member.memberGroup/delete', + destroy: 'member.memberGroup/destroy', + edit: 'member.memberGroup/edit', + recycle: 'member.memberGroup/recycle', + export: 'member.memberGroup/export', }, }; let Controller = { @@ -18,7 +18,7 @@ define(['table','form'], function (Table,Form) { Table.render({ elem: '#' + Table.init.table_elem, id: Table.init.tableId, - url: Fun.url(Table.init.requests.index_url), + url: Fun.url(Table.init.requests.index), init: Table.init, toolbar: ['refresh','add','destroy','export','recycle'], searchType:1, @@ -64,7 +64,7 @@ define(['table','form'], function (Table,Form) { Table.render({ elem: '#' + Table.init.table_elem, id: Table.init.tableId, - url: Fun.url(Table.init.requests.recycle_url), + url: Fun.url(Table.init.requests.recycle), init: Table.init, toolbar: ['refresh','delete','restore'], cols: [[ diff --git a/public/static/backend/js/member/memberlevel.js b/public/static/backend/js/member/memberlevel.js index dd15ffd4e..7c79e1ee7 100644 --- a/public/static/backend/js/member/memberlevel.js +++ b/public/static/backend/js/member/memberlevel.js @@ -10,14 +10,14 @@ define(['table','form','tableFilter'], function (Table,Form,tableFilter) { table_elem: 'list', tableId: 'list', requests:{ - modify_url:'member.memberLevel/modify', - index_url: 'member.memberLevel/index', - add_url: 'member.memberLevel/add', - edit_url: 'member.memberLevel/edit', - destroy_url: 'member.memberLevel/destroy', - delete_url: 'member.memberLevel/delete', - recycle_url: 'member.memberLevel/recycle', - export_url: 'member.memberLevel/export', + modify:'member.memberLevel/modify', + index: 'member.memberLevel/index', + add: 'member.memberLevel/add', + edit: 'member.memberLevel/edit', + destroy: 'member.memberLevel/destroy', + delete: 'member.memberLevel/delete', + recycle: 'member.memberLevel/recycle', + export: 'member.memberLevel/export', } }; //表格过滤示例 @@ -36,7 +36,7 @@ define(['table','form','tableFilter'], function (Table,Form,tableFilter) { Table.render({ elem: '#' + Table.init.table_elem, id: Table.init.tableId, - url: Fun.url(Table.init.requests.index_url), + url: Fun.url(Table.init.requests.index), init: Table.init, rowDouble:false, toolbar: ['refresh','add','destroy','export','recycle'], @@ -44,7 +44,7 @@ define(['table','form','tableFilter'], function (Table,Form,tableFilter) { cols: [[ {checkbox: true, }, {field: 'id', title: __('Id'), width: 80, sort: true}, - {field: 'name', title: __('Levelname'), width: 120, sort: true,filter:'xmSelect',extend:' data-url="member.memberLevel/index" data-tree="false" data-autorow="false" data-prop="name,name"'}, + {field: 'name', title: __('Levelname'), width: 120, sort: true,filter:'xmSelect',extend:'data-name="name" data-url="member.memberLevel/index" data-tree="false" data-autorow="false" data-prop="name,name"'}, {field: 'thumb', title: __('thumb'), width: 120, sort: true, templet: Table.templet.image}, {field: 'amount', title: __('Levelmoney'), width: 150, sort: true}, {field: 'discount', title: __('Leveldiscount'), width: 180, sort: true}, @@ -87,15 +87,15 @@ define(['table','form','tableFilter'], function (Table,Form,tableFilter) { table_elem: 'list', tableId: 'list', requests: { - recycle_url: 'member.member/recycle', - restore_url: 'member.member/restore', - delete_url: 'member.member/delete', + recycle: 'member.member/recycle', + restore: 'member.member/restore', + delete: 'member.member/delete', }, }; Table.render({ elem: '#' + Table.init.table_elem, id: Table.init.tableId, - url: Fun.url(Table.init.requests.index_url), + url: Fun.url(Table.init.requests.index), init: Table.init, toolbar: ['refresh','delete','restore'], cols: [[ diff --git a/public/static/backend/js/sys/adminlog.js b/public/static/backend/js/sys/adminlog.js index 176fa4c90..62519e7c2 100644 --- a/public/static/backend/js/sys/adminlog.js +++ b/public/static/backend/js/sys/adminlog.js @@ -6,9 +6,9 @@ define(['table','form'], function (Table,Form) { table_elem: 'list', tableId: 'list', requests: { - index_url: 'sys.adminlog/index', - delete_url: 'sys.adminlog/delete', - // edit_url: 'sys.adminlog/edit', + index: 'sys.adminlog/index', + delete: 'sys.adminlog/delete', + // edit: 'sys.adminlog/edit', edit_close: { type: 'open', 'class': 'layui-btn-xs layui-btn-normal', @@ -19,7 +19,7 @@ define(['table','form'], function (Table,Form) { btn:['close'], extend:"", } , - delall_url: { + delall: { type: 'delete', 'class': 'layui-btn-sm layui-btn-danger', icon: 'layui-icon layui-icon-delete', @@ -33,9 +33,9 @@ define(['table','form'], function (Table,Form) { Table.render({ elem: '#' + Table.init.table_elem, id: Table.init.tableId, - url: Fun.url(Table.init.requests.index_url), + url: Fun.url(Table.init.requests.index), init: Table.init, - toolbar: ['refresh', 'delete', 'delall_url'], + toolbar: ['refresh', 'delete', 'delall'], cols: [[ {type: "checkbox"}, {field: 'id', title: 'ID', sort: true, width: 80, search: false}, diff --git a/public/static/backend/js/sys/attach.js b/public/static/backend/js/sys/attach.js index e85c18376..99bacc3b0 100644 --- a/public/static/backend/js/sys/attach.js +++ b/public/static/backend/js/sys/attach.js @@ -3,8 +3,8 @@ define(['table','upload','form'], function (Table,Upload,Form) { table_elem: 'list', tableId: 'list', requests: { - index_url: 'sys.attach/index', - // add_url: 'sys.attach/add', + index: 'sys.attach/index', + // add: 'sys.attach/add', add_full:{ type: 'open', class: 'layui-btn-sm layui-btn-green', @@ -15,11 +15,11 @@ define(['table','upload','form'], function (Table,Upload,Form) { full: 0, extend:"data-btn='close'", }, - edit_url: 'sys.attach/edit', - delete_url: 'sys.attach/delete', - move_url: 'sys.attach/move', - modify_url: 'sys.attach/modify', - group_index_url:{ + edit: 'sys.attach/edit', + delete: 'sys.attach/delete', + move: 'sys.attach/move', + modify: 'sys.attach/modify', + group_index:{ type: 'open', class: 'layui-btn-sm layui-btn-green', url: 'sys.attachGroup/index', @@ -29,9 +29,9 @@ define(['table','upload','form'], function (Table,Upload,Form) { node:false, extend:"data-btn='false'", }, - group_add_url: 'sys.attachGroup/add', - group_edit_url: 'sys.attachGroup/edit', - group_delete_url: 'sys.attachGroup/delete', + group_add: 'sys.attachGroup/add', + group_edit: 'sys.attachGroup/edit', + group_delete: 'sys.attachGroup/delete', } } @@ -40,9 +40,9 @@ define(['table','upload','form'], function (Table,Upload,Form) { Table.render({ elem: '#' + Table.init.table_elem, id: Table.init.tableId, - url: Fun.url(Table.init.requests.index_url), + url: Fun.url(Table.init.requests.index), init: Table.init, - toolbar: ['refresh', 'add_full', 'delete','group_index_url'], + toolbar: ['refresh', 'add_full', 'delete','group_index'], cols: [[ {checkbox: true, }, {field: 'id', title: 'ID', width: 80 , sort: true}, @@ -153,7 +153,7 @@ define(['table','upload','form'], function (Table,Upload,Form) { title:elem.find('.layui-tree-txt').html(), '__token__': $("input[name='__token__']").val() } - Fun.ajax({url: Table.init.requests.group_add_url, data:postdata } + Fun.ajax({url: Table.init.requests.group_add, data:postdata } , function (res) { if (res.code > 0) { Fun.toastr.success(res.msg); @@ -167,7 +167,7 @@ define(['table','upload','form'], function (Table,Upload,Form) { title:elem.find('.layui-tree-txt').html(), '__token__': $("input[name='__token__']").val() } - Fun.ajax({url: Table.init.requests.group_edit_url, data: postdata}, function (res) { + Fun.ajax({url: Table.init.requests.group_edit, data: postdata}, function (res) { if (res.code > 0) { Fun.toastr.success(res.msg); } @@ -175,7 +175,7 @@ define(['table','upload','form'], function (Table,Upload,Form) { }) console.log(elem.find('.layui-tree-txt').html()); // 得到修改后的内容 } else if(type == 'del'){ // 删除节点 - Fun.ajax({url: Table.init.requests.group_delete_url, data: {ids: obj.data.id}} + Fun.ajax({url: Table.init.requests.group_delete, data: {ids: obj.data.id}} , function (res) { if (res.code > 0) { Fun.toastr.success(res.msg); @@ -206,7 +206,7 @@ define(['table','upload','form'], function (Table,Upload,Form) { Fun.toastr.error(__('please choose group')) return false; } - var Url =Fun.url(Table.init.requests.move_url); + var Url =Fun.url(Table.init.requests.move); var data = { ids: ids, '__token__': $("input[name='__token__']").val(), group_id:obj.data.id?obj.data.id:1, @@ -247,7 +247,7 @@ define(['table','upload','form'], function (Table,Upload,Form) { } layui.upload.render({ elem: '#uploadfile' - , url: Fun.url(Upload.init.requests.upload_url) + , url: Fun.url(Upload.init.requests.upload) , multiple: multiple , data: {group_id:group_id,path:upvalue.value.path} , before: function (obj) { @@ -292,7 +292,7 @@ define(['table','upload','form'], function (Table,Upload,Form) { Fun.toastr.error(__('please choose data')) return false; } - var Url =Fun.url(Table.init.requests.delete_url); + var Url =Fun.url(Table.init.requests.delete); var data = { ids: ids, '__token__': $("input[name='__token__']").val() }; @@ -316,7 +316,7 @@ define(['table','upload','form'], function (Table,Upload,Form) { var _this, id, name;_this = $(this).parent();id = _this.attr('data-id');name = _this.attr('title'); layui.layer.prompt({title: '修改图片名称', value: name, formType: 3}, function (value, index) { layui.layer.close(index); - var Url = Fun.url(Table.init.requests.edit_url+ '?id=' + id) ; + var Url = Fun.url(Table.init.requests.edit+ '?id=' + id) ; var data = { 'original_name': value, 'id': id, diff --git a/public/static/backend/js/sys/attachgroup.js b/public/static/backend/js/sys/attachgroup.js index ed252b124..9d5306ce4 100644 --- a/public/static/backend/js/sys/attachgroup.js +++ b/public/static/backend/js/sys/attachgroup.js @@ -6,16 +6,16 @@ define(['table','form'], function (Table,Form) { table_elem: 'list', tableId: 'list', requests: { - index_url: 'sys.attachGroup/index', - delete_url: 'sys.attachGroup/delete', - add_url: 'sys.attachGroup/add', - edit_url: 'sys.attachGroup/edit', + index: 'sys.attachGroup/index', + delete: 'sys.attachGroup/delete', + add: 'sys.attachGroup/add', + edit: 'sys.attachGroup/edit', } } Table.render({ elem: '#' + Table.init.table_elem, id: Table.init.tableId, - url: Fun.url(Table.init.requests.index_url), + url: Fun.url(Table.init.requests.index), init: Table.init, toolbar: ['refresh','add'], tree: { diff --git a/public/static/backend/js/sys/blacklist.js b/public/static/backend/js/sys/blacklist.js index 7fe2897bc..b04ccd8df 100644 --- a/public/static/backend/js/sys/blacklist.js +++ b/public/static/backend/js/sys/blacklist.js @@ -3,14 +3,14 @@ define(['table','form'], function (Table,Form) { table_elem: 'list', tableId: 'list', requests: { - index_url: 'sys.blacklist/index', - delete_url: 'sys.blacklist/delete', - add_url: 'sys.blacklist/add', - edit_url: 'sys.blacklist/edit', - recycle_url: 'sys.blacklist/recycle', - destroy_url: 'sys.blacklist/destroy', - restore_url: 'sys.blacklist/restore', - modify_url: 'sys.blacklist/modify', + index: 'sys.blacklist/index', + delete: 'sys.blacklist/delete', + add: 'sys.blacklist/add', + edit: 'sys.blacklist/edit', + recycle: 'sys.blacklist/recycle', + destroy: 'sys.blacklist/destroy', + restore: 'sys.blacklist/restore', + modify: 'sys.blacklist/modify', }, }; let Controller = { @@ -18,7 +18,7 @@ define(['table','form'], function (Table,Form) { Table.render({ elem: '#' + Table.init.table_elem, id: Table.init.tableId, - url: Fun.url(Table.init.requests.index_url), + url: Fun.url(Table.init.requests.index), init: Table.init, toolbar: ['refresh','add','delete','recycle'], cols: [[ @@ -61,7 +61,7 @@ define(['table','form'], function (Table,Form) { Table.render({ elem: '#' + Table.init.table_elem, id: Table.init.tableId, - url: Fun.url(Table.init.requests.index_url), + url: Fun.url(Table.init.requests.index), init: Table.init, toolbar: ['refresh','restore','destroy'], cols: [[ diff --git a/public/static/backend/js/sys/config.js b/public/static/backend/js/sys/config.js index e5478cf12..73dd0253c 100644 --- a/public/static/backend/js/sys/config.js +++ b/public/static/backend/js/sys/config.js @@ -5,11 +5,11 @@ define(['table','form'], function (Table,Form) { table_elem: 'list', tableId: 'list', requests: { - index_url: 'sys.config/index', - delete_url: 'sys.config/delete', - add_url: 'sys.config/add', - edit_url: 'sys.config/edit', - modify_url: 'sys.config/modify', + index: 'sys.config/index', + delete: 'sys.config/delete', + add: 'sys.config/add', + edit: 'sys.config/edit', + modify: 'sys.config/modify', setValue:{ type: 'open', class: 'layui-btn-xs layui-btn-danger', @@ -24,7 +24,7 @@ define(['table','form'], function (Table,Form) { Table.render({ elem: '#' + Table.init.table_elem, id: Table.init.tableId, - url: Fun.url(Table.init.requests.index_url), + url: Fun.url(Table.init.requests.index), init: Table.init, toolbar: ['refresh','add','delete'], cols: [[ @@ -52,8 +52,8 @@ define(['table','form'], function (Table,Form) { if(d.is_system){ $html = ''; var elem = '#'+d.LAY_COL.init.tableId; - if(Fun.checkAuth(Fun.common.getNode(Table.init.requests.edit_url),elem)) { - $html += ''; + if(Fun.checkAuth(Fun.common.getNode(Table.init.requests.edit),elem)) { + $html += ''; } if(Fun.checkAuth(Fun.common.getNode(Table.init.requests.setValue.url),elem)){ $html +=''; diff --git a/public/static/backend/js/sys/configgroup.js b/public/static/backend/js/sys/configgroup.js index 138413f1b..efbda002f 100644 --- a/public/static/backend/js/sys/configgroup.js +++ b/public/static/backend/js/sys/configgroup.js @@ -7,17 +7,17 @@ define(['table','form'], function (Table,Form) { table_elem: 'list', tableId: 'list', requests: { - index_url: 'sys.configGroup/index', - add_url: 'sys.configGroup/add', - edit_url: 'sys.configGroup/edit', - delete_url: 'sys.configGroup/delete', - modify_url: 'sys.configGroup/modify', + index: 'sys.configGroup/index', + add: 'sys.configGroup/add', + edit: 'sys.configGroup/edit', + delete: 'sys.configGroup/delete', + modify: 'sys.configGroup/modify', }, } Table.render({ elem: '#' + Table.init.table_elem, id: Table.init.tableId, - url: Fun.url(Table.init.requests.index_url), + url: Fun.url(Table.init.requests.index), init: Table.init, toolbar: ['refresh', 'add', 'delete'], cols: [[ diff --git a/public/static/backend/js/sys/languages.js b/public/static/backend/js/sys/languages.js index 4c0bef549..8932729df 100644 --- a/public/static/backend/js/sys/languages.js +++ b/public/static/backend/js/sys/languages.js @@ -6,16 +6,16 @@ define(['table','form'], function (Table,Form) { table_elem: 'list', tableId: 'list', requests: { - index_url: 'sys.languages/index', - add_url: 'sys.languages/add', - delete_url: 'sys.languages/delete', - edit_url: 'sys.languages/edit', + index: 'sys.languages/index', + add: 'sys.languages/add', + delete: 'sys.languages/delete', + edit: 'sys.languages/edit', } } Table.render({ elem: '#' + Table.init.table_elem, id: Table.init.tableId, - url: Fun.url(Table.init.requests.index_url), + url: Fun.url(Table.init.requests.index), init: Table.init, toolbar: ['refresh','add','delete'], cols: [[ diff --git a/public/static/frontend/.DS_Store b/public/static/frontend/.DS_Store new file mode 100644 index 000000000..6e72e6482 Binary files /dev/null and b/public/static/frontend/.DS_Store differ diff --git a/public/static/frontend/js/member.js b/public/static/frontend/js/member.js index 607598af5..3ed44349f 100644 --- a/public/static/frontend/js/member.js +++ b/public/static/frontend/js/member.js @@ -18,7 +18,7 @@ define(['jquery', "form", 'toastr','upload'], function ($, Form, Toastr,Upload) Form.api.bindEvent($('form')); var uploadInst = layui.upload.render({ elem: '.upload-img' //绑定元素 - ,url: Fun.url(Upload.init.requests.upload_url) //上传接口 + ,url: Fun.url(Upload.init.requests.upload) //上传接口 ,done: function(res){ $('.upload-img').parents('.avatar-add').find('img').attr('src',res.url); $('.upload-img').find('input').val(res.url); diff --git a/public/static/frontend/js/sys/attach.js b/public/static/frontend/js/sys/attach.js index f82cd97b8..866b58635 100644 --- a/public/static/frontend/js/sys/attach.js +++ b/public/static/frontend/js/sys/attach.js @@ -3,8 +3,8 @@ define(['jquery','table','upload','form'], function (undefined,Table,Upload,Form table_elem: 'list', tableId: 'list', requests: { - index_url: 'sys.attach/index', - // add_url: 'sys.attach/add', + index: 'sys.attach/index', + // add: 'sys.attach/add', add_full:{ type: 'open', class: 'layui-btn-sm layui-btn-green', @@ -15,13 +15,13 @@ define(['jquery','table','upload','form'], function (undefined,Table,Upload,Form full: 0, extend:"data-btn='close'", }, - edit_url: 'sys.attach/edit', - delete_url: 'sys.attach/delete', - move_url: 'sys.attach/move', - modify_url: 'sys.attach/modify', - group_add_url: 'sys.attachGroup/add', - group_edit_url: 'sys.attachGroup/edit', - group_delete_url: 'sys.attachGroup/delete', + edit: 'sys.attach/edit', + delete: 'sys.attach/delete', + move: 'sys.attach/move', + modify: 'sys.attach/modify', + group_add: 'sys.attachGroup/add', + group_edit: 'sys.attachGroup/edit', + group_delete: 'sys.attachGroup/delete', } } @@ -30,7 +30,7 @@ define(['jquery','table','upload','form'], function (undefined,Table,Upload,Form Table.render({ elem: '#' + Table.init.table_elem, id: Table.init.tableId, - url: Fun.url(Table.init.requests.index_url), + url: Fun.url(Table.init.requests.index), init: Table.init, toolbar: ['refresh', 'add_full', 'delete'], cols: [[ @@ -142,7 +142,7 @@ define(['jquery','table','upload','form'], function (undefined,Table,Upload,Form title:elem.find('.layui-tree-txt').html(), '__token__': $("input[name='__token__']").val() } - Fun.ajax({url: Table.init.requests.group_add_url, data:postdata } + Fun.ajax({url: Table.init.requests.group_add, data:postdata } , function (res) { if (res.code > 0) { Fun.toastr.success(res.msg); @@ -156,7 +156,7 @@ define(['jquery','table','upload','form'], function (undefined,Table,Upload,Form title:elem.find('.layui-tree-txt').html(), '__token__': $("input[name='__token__']").val() } - Fun.ajax({url: Table.init.requests.group_edit_url, data: postdata}, function (res) { + Fun.ajax({url: Table.init.requests.group_edit, data: postdata}, function (res) { if (res.code > 0) { Fun.toastr.success(res.msg); } @@ -164,7 +164,7 @@ define(['jquery','table','upload','form'], function (undefined,Table,Upload,Form }) console.log(elem.find('.layui-tree-txt').html()); // 得到修改后的内容 } else if(type == 'del'){ // 删除节点 - Fun.ajax({url: Table.init.requests.group_delete_url, data: {ids: obj.data.id}} + Fun.ajax({url: Table.init.requests.group_delete, data: {ids: obj.data.id}} , function (res) { if (res.code > 0) { Fun.toastr.success(res.msg); @@ -195,7 +195,7 @@ define(['jquery','table','upload','form'], function (undefined,Table,Upload,Form Fun.toastr.error(__('please choose group')) return false; } - var Url =Fun.url(Table.init.requests.move_url); + var Url =Fun.url(Table.init.requests.move); var data = { ids: ids, '__token__': $("input[name='__token__']").val(), group_id:obj.data.id?obj.data.id:1, @@ -236,7 +236,7 @@ define(['jquery','table','upload','form'], function (undefined,Table,Upload,Form } layui.upload.render({ elem: '#uploadfile' - , url: Fun.url(Upload.init.requests.upload_url) + , url: Fun.url(Upload.init.requests.upload) , multiple: multiple , data: {group_id:group_id,path:upvalue.value.path} , before: function (obj) { @@ -281,7 +281,7 @@ define(['jquery','table','upload','form'], function (undefined,Table,Upload,Form Fun.toastr.error(__('please choose data')) return false; } - var Url =Fun.url(Table.init.requests.delete_url); + var Url =Fun.url(Table.init.requests.delete); var data = { ids: ids, '__token__': $("input[name='__token__']").val() }; @@ -305,7 +305,7 @@ define(['jquery','table','upload','form'], function (undefined,Table,Upload,Form var _this, id, name;_this = $(this).parent();id = _this.attr('data-id');name = _this.attr('title'); layui.layer.prompt({title: '修改图片名称', value: name, formType: 3}, function (value, index) { layui.layer.close(index); - var Url = Fun.url(Table.init.requests.edit_url+ '?id=' + id) ; + var Url = Fun.url(Table.init.requests.edit+ '?id=' + id) ; var data = { 'original_name': value, 'id': id, diff --git a/public/static/js/backend.js b/public/static/js/backend.js index 7a8cb55ed..c14ea9cdc 100644 --- a/public/static/js/backend.js +++ b/public/static/js/backend.js @@ -1,267 +1,24 @@ -define([], function () { - var $ = layui.$, element = layui.element; - var $document = $(document), $container = $('#fun-app'), FUN_APP = 'fun-app', - THIS = 'layui-this', SIDE_SHRINK = 'layui-side-shrink', - //主题配置 - THEME = [ - { - headerBg: '#fff', - headerfontColor: '#595959', - headerBgThis: '#4d70ff', - headerBgLogo: '#0c0c0c', - headerLogofontColor: '#fff', - menuLeftBg: '#23262e', - menuLeftBgThis: '#4d70ff', - menuLeftBgHover: '#4d70ff', - menuLeftDlBg: 'rgba(0,0,0,.3)', - menuLeftfontColor: '#fff', - menuLeftfontColorHover: '#fff', - }, { - headerBg: '#fff', - headerfontColor: '#595959', - headerBgThis: '#197971', - headerBgLogo: '#0c0c0c', - headerLogofontColor: '#fff', - menuLeftBg: '#23262e', - menuLeftBgThis: '#20b598', - menuLeftBgHover: '#20b598', - menuLeftDlBg: 'rgba(0,0,0,.3)', - menuLeftfontColor: '#fff', - menuLeftfontColorHover: '#fff', - - }, { - headerBg: '#fff', - headerfontColor: '#595959', - headerBgThis: '#8e10b9', - headerBgLogo: '#0c0c0c', - headerLogofontColor: '#fff', - menuLeftBg: '#23262e', - menuLeftBgThis: '#8e10b9', - menuLeftBgHover: '#8e10b9', - menuLeftDlBg: 'rgba(0,0,0,.3)', - menuLeftfontColor: '#fff', - menuLeftfontColorHover: '#fff', - - }, { - headerBg: '#fff', - headerfontColor: '#595959', - headerBgThis: '#b8279f', - headerBgLogo: '#fff', - headerLogofontColor: '#595959', - menuLeftBg: '#23262e', - menuLeftBgThis: '#b8279f', - menuLeftBgHover: '#b8279f', - menuLeftDlBg: 'rgba(0,0,0,.3)', - menuLeftfontColor: '#fff', - menuLeftfontColorHover: '#fff', - }, { - headerBg: '#fff', - headerfontColor: '#595959', - headerBgThis: '#197971', - headerBgLogo: '#fff', - headerLogofontColor: '#595959', - menuLeftBg: '#23262e', - menuLeftBgThis: '#20b598', - menuLeftBgHover: '#20b598', - menuLeftDlBg: 'rgba(0,0,0,.3)', - menuLeftfontColor: '#fff', - menuLeftfontColorHover: '#fff', - }, { - headerBg: '#fff', - headerfontColor: '#595959', - headerBgThis: '#8e10b9', - headerBgLogo: '#fff', - headerLogofontColor: '#595959', - menuLeftBg: '#23262e', - menuLeftBgThis: '#8e10b9', - menuLeftBgHover: '#8e10b9', - menuLeftDlBg: 'rgba(0,0,0,.3)', - menuLeftfontColor: '#fff', - menuLeftfontColorHover: '#fff', - }, { - headerBg: '#20b598', - headerfontColor: '#fff', - headerBgThis: '#197971', - headerBgLogo: '#0c0c0c', - headerLogofontColor: '#fff', - menuLeftBg: '#23262e', - menuLeftBgThis: '#20b598', - menuLeftBgHover: '#20b598', - menuLeftDlBg: 'rgba(0,0,0,.3)', - menuLeftfontColor: '#fff', - menuLeftfontColorHover: '#fff', - }, { - headerBg: '#20b598', - headerfontColor: '#fff', - headerBgThis: '#197971', - headerBgLogo: '#20b598', - headerLogofontColor: '#fff', - menuLeftBg:'#23262e', - menuLeftBgThis: '#20b598', - menuLeftBgHover: '#20b598', - menuLeftDlBg: 'rgba(0,0,0,.3)', - menuLeftfontColor: '#fff', - menuLeftfontColorHover: '#fff', - }, { - headerBg: '#00a65a', - headerBgThis: '#197971', - headerBgLogo: '#00a65a', - headerLogofontColor: '#fff', - menuLeftBg: '#222d32', - menuLeftBgThis: '#00a65a', - menuLeftBgHover: '#00a65a', - menuLeftDlBg: 'rgba(0,0,0,.3)', - menuLeftfontColor: '#fff', - menuLeftfontColorHover: '#fff', - }, { - headerBg: '#8e10b9', - headerfontColor: '#fff', - headerBgThis: '#197971', - headerBgLogo: '#8e10b9', - headerLogofontColor: '#fff', - menuLeftBg:'#23262e', - menuLeftBgThis: '#8e10b9', - menuLeftBgHover: '#8e10b9', - menuLeftDlBg: 'rgba(0,0,0,.3)', - menuLeftfontColor: '#fff', - menuLeftfontColorHover: '#fff', - }, { - headerBg: '#23262e', - headerfontColor: '#fff', - headerBgThis: '#0c0c0c', - headerBgLogo: '#0c0c0c', - headerLogofontColor: '#fff', - menuLeftBg: '#23262e', - menuLeftBgThis: '#20b598', - menuLeftBgHover: '#3b3f4b', - menuLeftDlBg: 'rgba(0,0,0,.3)', - menuLeftfontColor: '#fff', - menuLeftfontColorHover: '#fff', - }, { - headerBg: '#ffa4d1', - headerfontColor: '#fff', - headerBgThis: '#bf7b9d', - headerBgLogo: '#e694bd', - headerLogofontColor: '#fff', - menuLeftBg: '#1f1f1f', - menuLeftBgThis: '#ffa4d1', - menuLeftBgHover: '#ffa4d1', - menuLeftDlBg: 'rgba(0,0,0,.3)', - menuLeftfontColor: '#fff', - menuLeftfontColorHover: '#fff', - }, { - headerBg: '#4d70ff', - headerfontColor: '#fff', - headerBgThis: '#0069b7', - headerBgLogo: '#0c0c0c', - headerLogofontColor: '#fff', - menuLeftBg: '#1f1f1f', - menuLeftBgThis: '#4d70ff', - menuLeftBgHover: '#4d70ff', - menuLeftDlBg: 'rgba(0,0,0,.3)', - menuLeftfontColor: '#fff', - menuLeftfontColorHover: '#fff', - }, { - headerBg: '#963885', - headerfontColor: '#fff', - headerBgThis: '#b8279f', - headerBgLogo: '#000', - headerLogofontColor: '#fff', - menuLeftBg: '#191a23', - menuLeftBgThis: '#963885', - menuLeftBgHover: '#963885', - menuLeftDlBg: 'rgba(0,0,0,.3)', - menuLeftfontColor: '#fff', - menuLeftfontColorHover: '#fff', - }, { - headerBg: '#963885', - headerfontColor: '#fff', - headerBgThis: '#b8279f', - headerBgLogo: '#b8279f', - headerLogofontColor: '#fff', - menuLeftBg: '#191a23', - menuLeftBgThis: '#963885', - menuLeftBgHover: '#963885', - menuLeftDlBg: 'rgba(0,0,0,.3)', - menuLeftfontColor: '#fff', - menuLeftfontColorHover: '#fff', - }, { - headerBg: '#4d70ff', - headerfontColor: '#fff', - headerBgThis: '#4d70ff', - headerBgLogo: '#4d70ff', - headerLogofontColor: '#fff', - menuLeftBg: '#1f1f1f', - menuLeftBgThis: '#4d70ff', - menuLeftBgHover: '#4d70ff', - menuLeftDlBg: 'rgba(0,0,0,.3)', - menuLeftfontColor: '#fff', - menuLeftfontColorHover: '#fff', - }, { - headerBg: '#ffb800', - headerfontColor: '#fff', - headerBgThis: '#d09600', - headerBgLogo: '#000', - headerLogofontColor: '#fff', - menuLeftBg:'#23262e', - menuLeftBgThis: '#ffb800', - menuLeftBgHover: '#ffb800', - menuLeftDlBg: 'rgba(0,0,0,.3)', - menuLeftfontColor: '#fff', - menuLeftfontColorHover: '#fff', - }, { - headerBg: '#ffb800', - headerfontColor: '#fff', - headerBgThis: '#d09600', - headerBgLogo: '#d09600', - headerLogofontColor: '#fff', - menuLeftBg:'#23262e', - menuLeftBgThis: '#ffb800', - menuLeftBgHover: '#ffb800', - menuLeftDlBg: 'rgba(0,0,0,.3)', - menuLeftfontColor: '#fff', - menuLeftfontColorHover: '#fff', - }, { - headerBg: '#e82121', - headerfontColor: '#fff', - headerBgThis: '#ae1919', - headerBgLogo: '#0c0c0c', - headerLogofontColor: '#fff', - menuLeftBg: '#1f1f1f', - menuLeftBgThis: '#e82121', - menuLeftBgHover: '#e82121', - menuLeftDlBg: 'rgba(0,0,0,.3)', - menuLeftfontColor: '#fff', - menuLeftfontColorHover: '#fff', - }, { - headerBg: '#e82121', - headerfontColor: '#fff', - headerBgThis: '#ae1919', - headerBgLogo: '#d91f1f', - headerLogofontColor: '#fff', - menuLeftBg: '#1f1f1f', - menuLeftBgThis: '#e82121', - menuLeftBgHover: '#e82121', - menuLeftDlBg: 'rgba(0,0,0,.3)', - menuLeftfontColor: '#fff', - menuLeftfontColorHover: '#fff', - }, { - headerBg: '#fff', - headerfontColor: '#000', - headerBgThis: '#e2ebfb', - headerBgLogo: '#fff', - headerLogofontColor: '#000', - menuLeftBg: '#fff', - menuLeftBgThis: '#e2ebfb', - menuLeftBgHover: '#e2ebfb', - menuLeftDlBg: '#fff', - menuLeftfontColor: '#000', - menuLeftfontColorHover: '#e2ebfb', - } - ]; +define(['theme-config'], function(themeConfig) { // 添加依赖 + var $ = layui.$, + tabs = layui.tabs, + $document = $(document), + $container = $('#fun-app'), + FUN_APP = 'fun-app', + THIS = 'layui-this', + SIDE_SHRINK = 'layui-side-shrink', + TABS = 'layui-tabs', + TABS_HEADER = 'layui-tabs-header', + CONTEXTMENU =''; + const COLOR_ID = 'COLOR_ID' , + LOCK_SCREEN ='LOCK_SCREEN', + SET_FRAME_THEME ='SET_FRAME_THEME', + SET_TABS ='SET_TABS', + SITE_THEME ='SITE_THEME'; + // 主题配置数组 - 保持原样 + var THEME = themeConfig; // 使用导入的配置 var Backend = { /*** 版本*/ - v: '5.0', + v: '6.0', /** * @param options */ @@ -270,22 +27,22 @@ define([], function () { options.themeid = options.themeid || 0; options.maxTabs = options.maxTabs || 15; THEME = options.theme ?options.theme:THEME; - Backend.initTabs({ - filter: 'layui-layout-tabs', - maxTabs: options.maxTabs, - }); + Backend.api.bindEvent(); + Backend.initTabs({id: TABS, maxTabs: options.maxTabs}); Backend.hideLoading(options.loadingTime); Backend.initBodyTheme(); Backend.initBgColor(); - Backend.api.bindEvent(); + //菜单自适应 + Backend.initNav(); }, initBodyTheme:function (name){ if(Config.site.site_theme ==0){ return false; } name = typeof name==='undefined'?'setTab':name; - $('.layui-side-menu .layui-nav-item').removeClass('layui-nav-hover'); - $('.layui-side-menu .layui-nav-item').find('dl').removeClass('layui-nav-child-drop').removeAttr('style'); + $item = $('.layui-side-menu .layui-nav-item'); + $item.removeClass('layui-nav-hover'); + $item.find('dl').removeClass('layui-nav-child-drop').removeAttr('style'); if($('.layui-layout-admin .layui-nav-header').length>0){ height = $('.layui-nav-header ul').height();//横屏 $('.layui-layout-admin .layui-pagetabs').attr('style','top:'+(60+height)+'px!important;'); @@ -293,13 +50,13 @@ define([], function () { } value = Fun.api.getStorage(name); if(value && value == 1) { - $('.layui-tabs-control.layui-icon-prev,.layui-tabs-control.layui-icon-next,.layui-tabs-control.layui-icon-down,#layui-tab-header').removeClass( + $("#"+TABS_HEADER).removeClass( 'layui-hide'); $('#layui-app-body').animate({ top: '40px' }, 100); }else if(value && value == 2){ - $('.layui-tabs-control.layui-icon-prev,.layui-tabs-control.layui-icon-next,.layui-tabs-control.layui-icon-down,#layui-tab-header').addClass( + $("#"+TABS_HEADER).addClass( 'layui-hide'); $('#layui-app-body').animate({ top: 0 @@ -311,13 +68,13 @@ define([], function () { hideLoading: function (time) { time = time || 200; var colorId = Backend.getColorId(); - theme = Fun.api.getStorage('setFrameTheme'); + theme = Fun.api.getStorage(SET_FRAME_THEME); var bg = THEME[colorId]['menuLeftBgThis']; if (colorId && theme) $(document).find('.fun-loading').find('span').css('background-color', THEME[colorId]['menuLeftBgHover']); setTimeout(function () { //判断是否锁定了界面 $(document).find('.fun-loading').fadeOut(); - if (Fun.api.getStorage('BackendLock')) { + if (Fun.api.getStorage(LOCK_SCREEN)) { title = [__('Input Password')]; if(theme) title[1] = 'background:' + THEME[colorId]['menuLeftBgThis'] + ';color:' + THEME[colorId]['menuLeftfontColor']; @@ -336,10 +93,10 @@ define([], function () { Fun.toastr.error(__('Input Password')); return false; } else { - if (value === Fun.api.getStorage('BackendLock')) { + if (value === Fun.api.getStorage(LOCK_SCREEN)) { Fun.toastr.close(index); //清除密码 - Fun.api.setStorage('BackendLock', null); + Fun.api.setStorage(LOCK_SCREEN, null); Fun.toastr.success(__('Unlock Success')) $('#lock-screen').remove(); } else { @@ -353,17 +110,14 @@ define([], function () { }, /** tab*/ initTabs: function (options) { - options.filter = options.filter || null; - options.maxTabs = options.maxTabs || 15; - Backend.listenScroll(); - Backend.listenSwitch(options); + options.id = options.id || TABS; + tabs.render({ elem: '#layui-tabs',header: ["#"+TABS_HEADER, '>li'],body: ['.layui-tabs-body', '>div'],}); Backend.listenTabs(options); - Backend.listenDeltab(options); Backend.listenFrameTheme(); }, initNav: function (options) { //菜单自适应 - var nav = $('.layui-tab .layui-tab-title'); + var nav = $('.layui-tab .layui-tabs-header'); if(Config.site.site_theme<=0 ){ if( $(window).width()<=769) { nav.width('100%'); return; @@ -371,9 +125,16 @@ define([], function () { pagetab = $('.layui-pagetabs').outerWidth(); right = $('.layui-nav.layui-layout-right').outerWidth(); left = $('.layui-nav.layui-layout-left').outerWidth(); - nav.width(pagetab - right- left); + nav.width(pagetab - right- left+50); } }, + initSession: function () { + layId = $('#layui-app-tabs .layui-tabs .layui-tabs-header').find('.layui-this').attr('lay-id'); + if(!layId){ + layId = $('#layui-app-tabs .layui-tabs .layui-tabs-header').children('li:last').attr('lay-id'); + } + layui.sessionData('tabLayId', {key: 'id', value: layId}) + }, //全屏 fullScreen: function () { var ele = document.documentElement @@ -406,11 +167,10 @@ define([], function () { $(window).trigger("resize"); }, listenTabs: function (options) { - var funTabInfo = layui.sessionData("funTabInfo"); - var tabLayId = layui.sessionData('tabLayId'); - var layId = tabLayId.id; - if (layId === null || layId === undefined) return false; - if (funTabInfo) { + var tabLayId = layui.sessionData("tabLayId"); + if (tabLayId) { + var layId = tabLayId.id; + if (layId === null || layId === undefined) return false; $menu = $("[lay-filter='menulist']").find("a[lay-id='"+layId+"']"); if($menu.length>0){ var text = $menu.attr('data-tips') || $menu.attr('title'), @@ -422,35 +182,12 @@ define([], function () { icon: icon, maxTabs: options.maxTabs, }); - element.tabChange('layui-layout-tabs', layId); + Backend.listenSwitchTabs(layId); } } //菜单自适应 Backend.initNav(); }, - /** - * 删除tab窗口 - * @param layId - * @param isParent - */ - delTab: function (layId, isParent) { - var funTabInfo = layui.sessionData("funTabInfo"); - if (funTabInfo != null) { - layui.sessionData("funTabInfo", {key: layId, remove: true}) - } - if (isParent === true) { - parent.layui.element.tabDelete('layui-tabs', layId); - } else { - element.tabDelete('layui-layout-tabs', layId); - } - layId = $('#layui-app-tabs .layui-tab .layui-tab-title').find('.layui-this').attr('lay-id'); - if(!layId){ - layId = $('#layui-app-tabs .layui-tab .layui-tab-title').children('li:last').attr('lay-id'); - } - element.tabChange('layui-layout-tabs', layId); - Backend.changeSessioinTabId(layId); - $('#layui-nav-righmenu').remove(); - }, /** * 增加tab 公用接口 */ @@ -460,71 +197,61 @@ define([], function () { options.text = options.text || ''; options.icon = Config.site.site_tabicon>0 ? options.icon : 'layui-tab-icon-active'; options.iframe = options.iframe || null; - if (top.window.$("#layui-app-tabs .layui-tab .layui-tab-title li").length >= options.maxTabs) { + if (top.window.$("#layui-app-tabs .layui-tabs .layui-tabs-header li").length >= options.maxTabs) { Fun.toastr.error(__('window is create by maxnum')); return false; } - Backend.changeSessioinTabId(options.layId); - layui.sessionData('funTabInfo', { - key: options.layId, - value: options.layId, - }); - var ele = element; - if (options.iframe) ele = top.layui.element; + + if (options.iframe) tabs = top.layui.tabs; var checkLayId = Backend.checkLayId(options.layId), loadindex; if(!options.layId) return false; if (!checkLayId) { loadindex = layui.layer.load(); - ele.tabAdd('layui-layout-tabs', { + tabs.add(TABS, { title: ' ' + options.text + '' //标题 , content: '' , id: options.layId, + index: options.layId, + done: function(params) { + // 给新标签头添加上下文菜单 + layui.dropdown.render($.extend({}, CONTEXTMENU.config, { + elem: params.headerItem // 当前标签头元素 + })); + } }); + Backend.initSession(options.layId); } else { loadindex = layui.layer.load(); if(Config.site.site_reloadiframe){ - var current_iframe = window.$("#layui-app-tabs .layui-tab-content div").find("iframe[id='"+options.layId+"']"); + var current_iframe = window.$("#layui-app-tabs .layui-tabs-body div").find("iframe[id='"+options.layId+"']"); current_iframe.eq(0).attr('src',options.url) // current_iframe[0].contentWindow.location.reload(); } } if (Fun.api.checkScreen()) { - $container.removeClass(SIDE_SHRINK).addClass('fun-app') + $container.removeClass(SIDE_SHRINK).addClass(FUN_APP) } $('#layui-nav-righmenu').remove(); layui.layer.close(loadindex); - ele.tabChange('layui-layout-tabs', options.layId); + tabs.change(TABS, options.layId); Backend.initNav(); }, - //切换id - changeSessioinTabId: function (layId) { - if (layId) { - layui.sessionData('tabLayId', {key: 'id', value: layId}) - } else { - layui.sessionData('tabLayId', null); - } - }, /** * 判断tab窗口 */ checkLayId: function (layId) { // 判断选项卡上是否有 var checkId = false; - top.window.$("#layui-app-tabs .layui-tab .layui-tab-title li").each(function () { - var checklayId = $(this).attr('lay-id'); - if (checklayId != null && checklayId === layId) { - checkId = true; - } - }); + top.window.$("#layui-app-tabs .layui-tabs .layui-tabs-header li[lay-id='"+layId+"']").length > 0 ? checkId = true : checkId = false; return checkId !== false; }, /** * 获取颜色缓存 */ getColorId:function(){ - var colorId = Fun.api.getStorage('funColorId');colorId = colorId?colorId:0; + var colorId = Fun.api.getStorage(COLOR_ID);colorId = colorId?colorId:0; return colorId; }, /** @@ -587,62 +314,63 @@ define([], function () { '.layui-side-scroll .layui-nav .layui-nav-child a:hover:not(.layui-this){background-color:' + themeData.menuLeftBgHover + '!important;}\n' + '.layui-layout-admin .layui-nav-tree .layui-this,.layui-layout-admin .layui-nav-tree .layui-this>a,' + '.layui-layout-admin .layui-nav-tree .layui-nav-child dd.layui-this,.layui-layout-admin .layui-nav-tree .layui-nav-child dd.layui-this a {background-color: ' + themeData.menuLeftBgThis + ' !important;}\n' + - '.layui-pagetabs .layui-tab-title li:hover,.layui-pagetabs .layui-tab-title li.layui-this{ color:' + themeData.menuLeftBgThis + '!important;}\n' + - '.layui-pagetabs .layui-tab-title li.layui-this .layui-tab-icon-active{ background-color:' + themeData.menuLeftBgThis + '!important;}\n' + + '.layui-pagetabs .layui-tabs-header li:hover,.layui-pagetabs .layui-tabs-header li.layui-this{ color:' + themeData.menuLeftBgThis + '!important;}\n' + + '.layui-pagetabs .layui-tabs-header li.layui-this .layui-tab-icon-active{ background-color:' + themeData.menuLeftBgThis + '!important;}\n' + '.layui-layout-admin .layui-nav-tree .layui-nav-bar{background:' + themeData.menuLeftBgThis + '!important;}\n'; $('#fun-bg-color').html(styleHtml); Backend.listenFrameTheme(); }, - /** - * 监听tab切换 - * @param options - */ - listenSwitch: function (options) { - options.filter = options.filter || ''; - layui.element.on('tab(' + options.filter + ')', function(){ - var layId = $(this).attr('lay-id'); - Backend.changeSessioinTabId(layId); - Backend.listenSwitchIframe(layId); - Backend.scrollPostion(); - }); - }, //监听主题 listenFrameTheme:function(){ Fun.api.setFrameTheme(); }, - /** - * 监听tab删除 - * @param options - */ - listenDeltab: function (options) { - options.filter = options.filter || null; - element.on('tabDelete(' + options.filter + ')', function () { - var layId = $(this).parent().attr('lay-id'); - Backend.delTab(layId); - }); - }, - /** - * 监听左右滚动 - */ - listenScroll: function (type) { - var tabNav = $('#layui-app-tabs .layui-tab .layui-tab-title'); - var left = tabNav.scrollLeft(); - if (type === 'left') { - tabNav.animate({ - scrollLeft: left - 550 - }, 100); - } else { - tabNav.animate({ - scrollLeft: left + 550 - }, 100); - } + listenContextMenu:function(){ + CONTEXTMENU = layui.dropdown.render({ + elem: '#layui-tabs #layui-tabs-header li', + trigger: 'contextmenu', + data: [{ + title: '刷新当前', + action: 'refresh', + mode: 'this' + },{ + title: '关闭当前', + action: 'close', + mode: 'this', + }, { + title: '关闭其他', + action: 'close', + mode: 'other' + }, { + title: '关闭右侧', + action: 'close', + mode: 'right' + },{ + title: '关闭所有', + action: 'close', + mode: 'all' + }], + click: function (data, othis, event) { + var index = this.elem.index(); // 获取活动标签索引 + // layId = this.elem.find('.layui-this').attr('lay-id'); + // 刷新标签 + if (data.action === 'refresh') { + Backend.events[data.action] && Backend.events[data.action].call(this); + } else if (data.action === 'close') { // 关闭标签操作 + if (data.mode === 'this') { + tabs.close(TABS, index); // 关闭当前标签 + }else{ + tabs.closeMult(TABS, data.mode,index); + } + } + } + }); }, /** * 监听切换 左侧菜单的样式和多级菜单的展开 * @param layId */ - listenSwitchIframe: function (layId) { + listenSwitchTabs: function (layId) { $("#layui-side-left-menu").find("li,dd").removeClass("layui-this").removeClass("layui-nav-itemed");//关闭所有展开的菜单 $("#layui-side-left-menu > li dl.layui-nav-child").removeAttr('style'); $menu = $("#layui-side-left-menu a[lay-id='"+layId+"']"); @@ -658,23 +386,6 @@ define([], function () { } } }, - /** - * 自动定位 - */ - scrollPostion: function () { - var tabNav = $('#layui-app-tabs .layui-tab .layui-tab-title'); - var autoLeft = 0; - tabNav.children("li").each(function () { - if ($(this).hasClass(THIS)) { - return false; - } else { - autoLeft += $(this).outerWidth(); - } - }); - tabNav.animate({ - scrollLeft: autoLeft - tabNav.width() / 4 - }, 200); - }, events: { //弹出主题面板 opentheme: function () { @@ -750,9 +461,9 @@ define([], function () { content: html, }); data = { - 'site_theme':Fun.api.getStorage('siteTheme'), - 'setTab':Fun.api.getStorage('setTab'), - 'setFrameTheme':Fun.api.getStorage('setFrameTheme'), + 'site_theme':Fun.api.getStorage(SITE_THEME), + 'setTab':Fun.api.getStorage(SET_TABS), + 'setFrameTheme':Fun.api.getStorage(SET_FRAME_THEME), } data.site_theme?data.site_theme:0; layui.form.val("form", data); @@ -766,7 +477,7 @@ define([], function () { var colorId = othis.attr('data-color'); $('.layui-fun-color .color-content ul .layui-this').attr('class', ''); $(this).attr('class', THIS); - Fun.api.setStorage('funColorId',colorId); + Fun.api.setStorage(COLOR_ID,colorId); Backend.initBgColor(); }, /** @@ -774,7 +485,7 @@ define([], function () { */ lockScreen: function () { var colorId = Backend.getColorId(); - theme = Fun.api.getStorage('setFrameTheme'); + theme = Fun.api.getStorage(SET_FRAME_THEME); title = [__('Set Password To Lock Screen')]; if(theme) title[1] = 'background:' + THEME[colorId]['menuLeftBgThis'] + ';color:' + THEME[colorId]['menuLeftfontColor']; layui.layer.prompt({ @@ -791,7 +502,7 @@ define([], function () { Fun.toastr.error(__('Input Password')); return false; } else { - Fun.api.setStorage('BackendLock',value); + Fun.api.setStorage(LOCK_SCREEN,value); layui.layer.close(index); title = [__('Input Password')]; if(theme) title[1] = 'background:' + THEME[colorId]['menuLeftBgThis'] + ';color:' + THEME[colorId]['menuLeftfontColor']; @@ -808,11 +519,11 @@ define([], function () { Fun.toastr.error(__('Input Password')); return false; } else { - if (value === Fun.api.getStorage('BackendLock')) { + if (value === Fun.api.getStorage(LOCK_SCREEN)) { layui.layer.close(index); $(".yy").hide(); //清除密码 - Fun.api.setStorage('BackendLock', null); + Fun.api.setStorage(LOCK_SCREEN, null); Fun.toastr.success(__('Unlock Success')); $('#lock-screen').remove(); } else { @@ -830,12 +541,6 @@ define([], function () { flexible: function () { Backend.sideFlexible(); }, - rightPage: function () { - Backend.listenScroll('right') - }, - leftPage: function () { - Backend.listenScroll('left') - }, showtips: function (othis, type) { if ($container.hasClass(SIDE_SHRINK)) { if (type === 1) { @@ -865,12 +570,12 @@ define([], function () { Backend.sideFlexible(); }, clear: function (othis) { //清除缓存 - layui.sessionData('funTabInfo', + layui.sessionData('tabLayId', { key: 'fun-safe', value: '', }); - layui.sessionData('funTabInfo', + layui.sessionData('tabLayId', { key: 'fun-info', value: '', @@ -878,45 +583,22 @@ define([], function () { var url = othis.data('ajax') ? othis.data('ajax') : Backend.refreshUrl; Fun.ajax({url: url}, function (res) { Fun.toastr.success(res.msg,function(){ - $("#layui-app-tabs .layui-tab-content .layui-show").find("iframe")[0].contentWindow.location.reload(); + $("#layui-app-tabs .layui-tabs-body .layui-show").find("iframe")[0].contentWindow.location.reload(); }); }, function (res) { Fun.toastr.error(res.msg,function(){ - $("#layui-app-tabs .layui-tab-content .layui-show").find("iframe")[0].contentWindow.location.reload(); + $("#layui-app-tabs .layui-tabs-body .layui-show").find("iframe")[0].contentWindow.location.reload(); }); }) }, refresh: function () { //刷新 Fun.toastr.success(__('Refresh Success'), setTimeout(function () { - $("#layui-app-tabs .layui-tab-content .layui-show").find("iframe")[0].contentWindow.location.reload(); + $("#layui-app-tabs .layui-tabs-body .layui-show").find("iframe")[0].contentWindow.location.reload(); Fun.toastr.close(); }, 1200) ); - } - //关闭当前标签页 - , closeThisTabs: function () { - var layId = $("#layui-app-tabs .layui-tab .layui-tab-title li.layui-this").attr('lay-id'); - if (layId) { - Backend.delTab(layId) - } - } - //关闭其它标签页 - , closeOtherTabs: function (type) { - $("#layui-app-tabs .layui-tab .layui-tab-title li").each(function (key, val) { - var layId = $(val).attr('lay-id'); - if (type === 'all' && layId) { - Backend.delTab(layId); - } else { - if (layId && !$(this).hasClass(THIS)) { - Backend.delTab(layId); - } - } - }); - } - //关闭全部标签页 - , closeAllTabs: function () { - Backend.events.closeOtherTabs('all'); }, + /** * 退出登陆 * @param othis @@ -960,7 +642,7 @@ define([], function () { code = $(data.elem).attr('name'); Fun.ajax({url: url,data:{value:data.value,code:code}}, function (res) { Fun.toastr.success(res.msg, function () { - Fun.api.setStorage('siteTheme', data.value) + Fun.api.setStorage(SITE_THEME, data.value) window.location.reload(); }); }, function (res) { @@ -982,7 +664,7 @@ define([], function () { } }); layui.form.on('radio(setFrameTheme)', function (data) { - Fun.api.setStorage('setFrameTheme',data.value) + Fun.api.setStorage(SET_FRAME_THEME,data.value) window.location.reload(); }); //监听导航点击菜单点击*/ @@ -1000,12 +682,13 @@ define([], function () { $(this).parents('li').children('.layui-nav-child').removeClass('layui-show') } } - if($('#layui-side-left-menu').find('ul[menu-id="'+id+'"]').length>0){ + $menu = $('#layui-side-left-menu'); + if($menu.find('ul[menu-id="'+id+'"]').length>0){ $('#layui-app-body').addClass('layui-sub-body'); } - $('#layui-side-left-menu').removeClass('layui-hide');//四 - $('#layui-side-left-menu').find('ul[menu-id="'+id+'"]').removeClass('layui-hide'); - $('#layui-side-left-menu').find('ul[menu-id="'+id+'"]').siblings('ul').not('[data-rel="external"]').addClass('layui-hide'); + $menu.removeClass('layui-hide');//四 + $menu.find('ul[menu-id="'+id+'"]').removeClass('layui-hide'); + $menu.find('ul[menu-id="'+id+'"]').siblings('ul').not('[data-rel="external"]').addClass('layui-hide'); layui.layer.close(index) } }) @@ -1080,38 +763,18 @@ define([], function () { _t.siblings('dd').children('dl').removeClass('layui-nav-child-drop'); _t.find('dl').removeAttr('style'); }); - //鼠标右键盘 - layui.dropdown.render({ - elem: '#layui-tab-header' //也可绑定到 document,从而重置整个右键 - ,trigger: 'contextmenu' //contextmenu - ,isAllowSpread: false //禁止菜单组展开收缩 - ,style: 'width: 130px' //定义宽度,默认自适应 - ,id: 'layui-tab-header-menu' //定义唯一索引 - ,data: [{ - title: ' 刷新当前页', - templet: ' {{d.title}}' - ,id: 'refresh' - }, {type:'-'},{ - title: ' 关闭当前页', - templet: '  {{d.title}}' - ,id: 'closeThisTabs' - },{type:'-'},{ - title: ' 关闭其它页', - templet: '  {{d.title}}' - ,id: 'closeOtherTabs' - },{type:'-'},{ - title: ' 关闭全部页', - templet: '  {{d.title}}' - ,id: 'closeAllTabs' - }],click:function (obj, othis) { - var _t = othis.find('a'), attrEvent = obj.id; - Backend.events[attrEvent] && Backend.events[attrEvent].call(this, _t); - } + // tabs 关闭后的事件 + tabs.on('afterClose('+TABS+')', function(data) { + Backend.initSession(); + }); + tabs.on('afterChange('+TABS+')', function(data) { + Backend.listenSwitchTabs($(this).attr('lay-id')); }); $(window).on("resize", function () { Backend.initBodyTheme(); Backend.initNav(); }); + Backend.listenContextMenu(); $(window).trigger('resize'); }, } diff --git a/public/static/js/builder.js b/public/static/js/builder.js index a1ccfac8e..d96ee5ce4 100644 --- a/public/static/js/builder.js +++ b/public/static/js/builder.js @@ -14,9 +14,9 @@ define(['table', 'form'], function (Table, Form) { } } }; - switch (ACTION) { + switch (window.ACTION) { case 'index': - Controller[ACTION] = function () { + Controller[window.ACTION] = function () { if (typeof tableOptions === "object") { layui.each(tableOptions, function (i, v) { Table.init[i] = init = { @@ -38,7 +38,7 @@ define(['table', 'form'], function (Table, Form) { case 'add': case 'edit': case 'copy': - Controller[ACTION] = function () { + Controller[window.ACTION] = function () { if (typeof tableOptions === "object") { layui.each(tableOptions, function (i, v) { Table.init[i] = { @@ -59,7 +59,7 @@ define(['table', 'form'], function (Table, Form) { } break; case 'recycle': - Controller[ACTION] = function () { + Controller[window.ACTION] = function () { if (typeof tableOptions === "object") { layui.each(tableOptions, function (i, v) { Table.init[i] = { @@ -80,7 +80,7 @@ define(['table', 'form'], function (Table, Form) { break; default: - Controller[ACTION] = function () { + Controller[window.ACTION] = function () { if (typeof tableOptions === "object") { layui.each(tableOptions, function (i, v) { Table.init[i] = { diff --git a/public/static/js/fun.js b/public/static/js/fun.js index 93700c543..79381ef72 100644 --- a/public/static/js/fun.js +++ b/public/static/js/fun.js @@ -16,7 +16,7 @@ define(["lang",'toastr','dayjs'], function (Lang,Toastr,Dayjs) { url = url==undefined?location.href:url; var domain = window.location.host; if (url && url.indexOf(domain) !== -1) return url; - var ajax_url = Config.publicAjaxUrl;tempurl = url.split('?') ;//公共url + var ajax_url = Config.public_ajax_url;tempurl = url.split('?') ;//公共url var suffix = tempurl[1]?"?"+tempurl[1]:'',prefix = tempurl[0]; if($.inArray(prefix,ajax_url)!== -1) return Config.module+url prefix = prefix.indexOf('/')==0 ? prefix.replace('/',''):prefix; @@ -31,8 +31,12 @@ define(["lang",'toastr','dayjs'], function (Lang,Toastr,Dayjs) { } return url; }, + fixurl: function (url) { + return fun.url(url); + }, //替换ids replaceurl: function (url, d) { + if (!url) return url || ''; id = typeof d.primaryKeyValue !== 'undefined' ? d.primaryKeyValue : 0; //替换ids if (url) { @@ -169,7 +173,7 @@ define(["lang",'toastr','dayjs'], function (Lang,Toastr,Dayjs) { } }) //锁屏 - if($('#lock-screen').length>0 && !Fun.api.getStorage('BackendLock')){ + if($('#lock-screen').length>0 && !Fun.api.getStorage('LOCK_SCREEN')){ $('#lock-screen').remove(); } } @@ -302,6 +306,14 @@ define(["lang",'toastr','dayjs'], function (Lang,Toastr,Dayjs) { }); return index; }, + prompt: function (othis, msg, success) { + layui.layer.prompt({title: msg, formType: 2}, function(value, index, elem){ + if(value === '') return elem.focus(); + typeof success === 'function' && success.call(this,value,index,elem); + layer.close(index); + }); + return false; + }, // 关闭消息框 close: function (index) { if (index) {layui.layer.close(index);} else {layui.layer.closeAll();} @@ -336,10 +348,10 @@ define(["lang",'toastr','dayjs'], function (Lang,Toastr,Dayjs) { return false; }, open: function (othis) { - var data = othis.data(); - if(!othis.data('url') && !othis.data('href')) return ; + var data = Fun.api.getElementData(othis); + if(!data.url && !data.href) return ; var options = { - title: othis.prop('title') ? othis.prop('title') : (data.title || '') , + title: data.title ? data.title : (othis.prop('title') || '') , url: data.url ? data.url : data.href, width: data.width, height: data.height, @@ -354,13 +366,14 @@ define(["lang",'toastr','dayjs'], function (Lang,Toastr,Dayjs) { Fun.api.open(options); }, iframe:function(othis){ - var _t = othis - , url = _t.data('url') ? _t.data('url') : _t.data('iframe') - , layId = _t.attr('data-id') || _t.attr('lay-id') || url - , text = _t.attr('title') || _t.data('text') || _t.attr('lay-text') || _t.attr('lay-tips') - , icon = _t.find('i').attr('class') || 'layui-icon layui-icon-radio' - , iframe = !!_t.has('data-iframe'), - target = _t.prop('target') || '_self'; + var _t = othis, + data = Fun.api.getElementData(_t); + url = data.url ? data.url : data.iframe, + layId = data.id || data.layId || url, + text = data.text || _t.attr('lay-text') || _t.attr('lay-tips'), + icon = _t.find('i').attr('class') || 'layui-icon layui-icon-radio', + iframe = !!_t.has('data-iframe'), + target = data.target || _t.prop('target') || '_self'; url = Fun.url(url); if (!layId) { return false; @@ -369,12 +382,13 @@ define(["lang",'toastr','dayjs'], function (Lang,Toastr,Dayjs) { window.open(url, "_blank"); return false; } - options = {url: url, layId: layId, text: text, icon: icon, iframe: iframe, target: target,} + options = {url: url, layId: layId, text: text, icon: icon, iframe: iframe, target: target,width:data.width,height:data.height,isResize:data.resize,full:data.full,btn:data.btn,anim:data.anim,offset:data.offset,btnAlign:data.btnAlign,autoheight:data.autoheight,} Fun.api.iframe(options); } }, popconfirm:function(othis, options,Table){ - var data = othis.data(),value; + var data = Fun.api.getElementData(othis); + var dataValue= data.value; if (options) { title = options.title; url = options.url; @@ -384,7 +398,7 @@ define(["lang",'toastr','dayjs'], function (Lang,Toastr,Dayjs) { tableId = data.tableid; title = title || 'Are you sure to do this'; url = url !== undefined ? url : window.location.href; - tableId = tableId || Table.init.tableId, value = data.value; + tableId = tableId || Table.init.tableId, dataValue = data.value; } ids = ''; if(Table){ @@ -392,7 +406,7 @@ define(["lang",'toastr','dayjs'], function (Lang,Toastr,Dayjs) { ids = arr[0]; length = arr[1]; } - postdata = {ids:ids};if(value){postdata.value = value} + postdata = {ids:ids};if(dataValue){postdata.value = dataValue} Fun.toastr.popconfirm(othis,__(title), function () { Fun.ajax({url: url, data: postdata}, function (res) { Fun.toastr.success(res.msg, function () { @@ -418,8 +432,47 @@ define(["lang",'toastr','dayjs'], function (Lang,Toastr,Dayjs) { confirm:function(othis, options,Table){ return Fun.events.request(othis, options,Table); }, + prompt: function(othis, options,Table){ + var data = Fun.api.getElementData(othis); + var dataValue= data.value; + if (options) { + title = options.title; + url = options.url; + tableId = options.tableId || Table.init.tableId + } else { + var title = data.prompt || othis.prop('prompt') || othis.prop('text') || data.text || othis.prop('title') || data.title , url = data.url ? data.url : data.href, + tableId = data.tableid; + title = title || 'Are you sure to do this'; + url = url !== undefined ? url : window.location.href; + tableId = tableId || Table.init.tableId, dataValue = data.value; + } + ids = ''; + if(Table){ + arr = Table.getIds(url, tableId); + ids = arr[0]; + length = arr[1]; + } + postdata = {ids:ids};if(dataValue){postdata.value = dataValue} + Fun.toastr.prompt(othis,__(title), function (text,index,elem) { + if(text){ + postdata.text = text; + } + Fun.ajax({url: url, data: postdata}, function (res) { + Fun.toastr.success(res.msg, function () { + Table && Table.api.reload(tableId) + }) + }, function (res) { + Fun.toastr.error(res.msg, function () { + Table && Table.api.reload(tableId) + }) + }) + Fun.toastr.close() + }); + return false + }, request: function (othis, options,Table) { - var data = othis.data(),value; + var data = Fun.api.getElementData(othis); + var dataValue= data.value; if (options) { title = options.title; url = options.url; @@ -429,7 +482,7 @@ define(["lang",'toastr','dayjs'], function (Lang,Toastr,Dayjs) { tableId = data.tableid; title = title || 'Are you sure to do this'; url = url !== undefined ? url : window.location.href; - tableId = tableId || Table.init.tableId, value = data.value; + tableId = tableId || Table.init.tableId, dataValue = data.value; } ids = ''; if(Table){ @@ -437,7 +490,7 @@ define(["lang",'toastr','dayjs'], function (Lang,Toastr,Dayjs) { ids = arr[0]; length = arr[1]; } - postdata = {ids:ids};if(value){postdata.value = value} + postdata = {ids:ids};if(dataValue){postdata.value = dataValue} Fun.toastr.confirm(__(title), function () { Fun.ajax({url: url, data: postdata}, function (res) { Fun.toastr.success(res.msg, function () { @@ -461,7 +514,8 @@ define(["lang",'toastr','dayjs'], function (Lang,Toastr,Dayjs) { return false }, dropdown: function (othis,rowData,tableOption) { - var data = $(othis).data(); extend = data.extend; + var data = Fun.api.getElementData(othis); + var extend = data.extend || data.child || ''; var dropdowndata = []; if (typeof extend === 'object') { ele = '';d= ''; @@ -533,6 +587,17 @@ define(["lang",'toastr','dayjs'], function (Lang,Toastr,Dayjs) { 接口 */ api: { + //获取元素数据 + getElementData: function (othis) { + if(othis.length === undefined || othis.length<=0){ + othis = $(othis); + } + var data = othis.data(); + var options = othis.attr('options'); + options = options ? JSON.parse(options) : {}; + data = $.extend({}, options, data); + return data; + }, //删除空数据 removeEmptyData: function (obj) { $.each(obj, function(key, value) { @@ -574,9 +639,9 @@ define(["lang",'toastr','dayjs'], function (Lang,Toastr,Dayjs) { }, //设置子页面主题 setFrameTheme:function(body){ - var colorId = Fun.api.getStorage('funColorId'); + var colorId = Fun.api.getStorage('COLOR_ID'); colorId = colorId?colorId:0;theme = 'theme'+colorId; - if(Fun.api.getStorage('setFrameTheme')){ + if(Fun.api.getStorage('SET_FRAME_THEME')){ var iframe = $("#layui-tab .layui-tab-item").find("iframe"); for (var i = 0; i < iframe.length; i++) { $(iframe[i]).on('load',function(){ @@ -592,7 +657,7 @@ define(["lang",'toastr','dayjs'], function (Lang,Toastr,Dayjs) { layui.$('iframe').contents().find('body').attr('id',theme); } //测试 - themeData = Fun.api.getStorage('setFrameTheme'); + themeData = Fun.api.getStorage('SET_FRAME_THEME'); if(themeData){ $('body').addClass('active'); }else{ @@ -628,11 +693,16 @@ define(["lang",'toastr','dayjs'], function (Lang,Toastr,Dayjs) { var isResize = (options.isResize === undefined); var isFull = !!options.full;url = type===2?Fun.url(url):url; isResize = isResize === false ? true : isResize; - width = width || Config.site.site_layer_width ; - height = height || Config.site.site_layer_height; - width = width || '50%';height = height || '100%'; - width = /%|px/.test(width)?width:$(window).width()+20 >= width ? width + 'px' :'95%'; - height = /%|px/.test(height)?height:($(window).height()+110)>=height?height + 'px' :'100%'; + if(options.area){ + width = options.area[0]; + height = options.area[1]; + }else{ + width = width || Config.site.site_layer_width ; + height = height || Config.site.site_layer_height; + width = width || '50%';height = height || '100%'; + width = /%|px/.test(width)?width:$(window).width()+20 >= width ? width + 'px' :'95%'; + height = /%|px/.test(height)?height:($(window).height()+110)>=height?height + 'px' :'100%'; + } autoheight = autoheight ? true:false; offset = options.offset || Config.site.site_layer_offset ; anim = Config.site.site_layer_anim ? Config.site.site_layer_anim:options.anim; @@ -808,7 +878,7 @@ define(["lang",'toastr','dayjs'], function (Lang,Toastr,Dayjs) { if(typeof callback === 'string'){ eval(callback)(othis,rowData,tableOption); }else if(typeof callback === 'function'){ - callback(othis,rowData,tableOption); + callback(othis,rowData,tableOption); } } return true; diff --git a/public/static/js/require-backend.js b/public/static/js/require-backend.js index 85da6448d..204a2e9c1 100644 --- a/public/static/js/require-backend.js +++ b/public/static/js/require-backend.js @@ -20,7 +20,7 @@ require.config({ } ], baseUrl: BASE_URL, - include: ['jquery', 'css','jsoneditor','layCascader','tableSelect','tableFilter','iconPicker', 'toastr','step-lay','inputTags', 'timeago','selects','cityPicker','cxSelect', 'selectPage','xmSelect','autoComplete','Sortable','timePicker','croppers', 'backend','md5','fun','form','table','upload'], + include: ['jquery', 'css','jsoneditor','layCascader','tableSelect','tableFilter','iconPicker', 'toastr','step-lay','inputTags', 'timeago','selects','cityPicker','cxSelect', 'selectPage','xmSelect','autoComplete','Sortable','timePicker','croppers','theme-config', 'backend','md5','fun','form','table','upload'], paths: { 'lang' : 'empty:', 'jquery' : 'plugins/jquery/jquery-3.7.1.min', // jquery @@ -45,6 +45,7 @@ require.config({ 'autoComplete' : 'plugins/lay-module/autoComplete/autoComplete', 'Sortable' : 'plugins/lay-module/Sortable/Sortable.min', 'md5' : 'plugins/lay-module/md5/md5.min', // 后台扩展 + 'theme-config' : 'js/theme-config', // jquery 'backend' : 'js/backend', // fun后台扩展 'fun' : 'js/fun', // api扩展 'table' : 'js/require-table', diff --git a/public/static/js/require-backend.min.js b/public/static/js/require-backend.min.js index 8643bd33a..df54ad162 100644 --- a/public/static/js/require-backend.min.js +++ b/public/static/js/require-backend.min.js @@ -1,19 +1,19 @@ -function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function _possibleConstructorReturn(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function _inherits(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function _typeof(e){"@babel/helpers - typeof";return(_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function _toConsumableArray(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);tx.cacheLength&&delete t[e.shift()],t[n+" "]=i}}function i(e){return e[$]=!0,e}function o(e){var t=k.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function a(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&&se(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function s(e){return i(function(t){return t=+t,i(function(n,i){for(var o,r=e([],n.length,t),a=r.length;a--;)n[o=r[a]]&&(n[o]=!(i[o]=n[o]))})})}function l(e){return e&&void 0!==e.getElementsByTagName&&e}function c(e){var n,i=e?e.ownerDocument||e:Ie;return i!=k&&9===i.nodeType&&i.documentElement&&(_=(k=i).documentElement,S=!ge.isXMLDoc(k),E=_.matches||_.webkitMatchesSelector||_.msMatchesSelector,_.msMatchesSelector&&Ie!=k&&(n=k.defaultView)&&n.top!==n&&n.addEventListener("unload",ae),le.getById=o(function(e){return _.appendChild(e).id=ge.expando,!k.getElementsByName||!k.getElementsByName(ge.expando).length}),le.disconnectedMatch=o(function(e){return E.call(e,"*")}),le.scope=o(function(){return k.querySelectorAll(":scope")}),le.cssHas=o(function(){try{return k.querySelector(":has(*,:jqfake)"),!1}catch(e){return!0}}),le.getById?(x.filter.ID=function(e){var t=e.replace(ie,oe);return function(e){return e.getAttribute("id")===t}},x.find.ID=function(e,t){if(void 0!==t.getElementById&&S){var n=t.getElementById(e);return n?[n]:[]}}):(x.filter.ID=function(e){var t=e.replace(ie,oe);return function(e){var n=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},x.find.ID=function(e,t){if(void 0!==t.getElementById&&S){var n,i,o,r=t.getElementById(e);if(r){if((n=r.getAttributeNode("id"))&&n.value===e)return[r];for(o=t.getElementsByName(e),i=0;r=o[i++];)if((n=r.getAttributeNode("id"))&&n.value===e)return[r]}return[]}}),x.find.TAG=function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):t.querySelectorAll(e)},x.find.CLASS=function(e,t){if(void 0!==t.getElementsByClassName&&S)return t.getElementsByClassName(e)},T=[],o(function(e){var t;_.appendChild(e).innerHTML="",e.querySelectorAll("[selected]").length||T.push("\\["+be+"*(?:value|"+F+")"),e.querySelectorAll("[id~="+$+"-]").length||T.push("~="),e.querySelectorAll("a#"+$+"+*").length||T.push(".#.+[+~]"),e.querySelectorAll(":checked").length||T.push(":checked"),(t=k.createElement("input")).setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),_.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&T.push(":enabled",":disabled"),(t=k.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||T.push("\\["+be+"*name"+be+"*="+be+"*(?:''|\"\")")}),le.cssHas||T.push(":has"),T=T.length&&new RegExp(T.join("|")),B=function(e,n){if(e===n)return A=!0,0;var i=!e.compareDocumentPosition-!n.compareDocumentPosition;return i||(1&(i=(e.ownerDocument||e)==(n.ownerDocument||n)?e.compareDocumentPosition(n):1)||!le.sortDetached&&n.compareDocumentPosition(e)===i?e===k||e.ownerDocument==Ie&&t.contains(Ie,e)?-1:n===k||n.ownerDocument==Ie&&t.contains(Ie,n)?1:w?ne.call(w,e)-ne.call(w,n):0:4&i?-1:1)}),k}function d(){}function u(e,n){var i,o,r,a,s,l,c,d=N[e+" "];if(d)return n?0:d.slice(0);for(s=e,l=[],c=x.preFilter;s;){for(a in i&&!(o=G.exec(s))||(o&&(s=s.slice(o[0].length)||s),l.push(r=[])),i=!1,(o=Z.exec(s))&&(i=o.shift(),r.push({value:i,type:o[0].replace(Ce," ")}),s=s.slice(i.length)),x.filter)!(o=Y[a].exec(s))||c[a]&&!(o=c[a](o))||(i=o.shift(),r.push({value:i,type:a,matches:o}),s=s.slice(i.length));if(!i)break}return n?s.length:s?t.error(e):N(e,l).slice(0)}function h(e){for(var t=0,n=e.length,i="";t+~]|"+be+")"+be+"*"),z=new RegExp(be+"|>"),K=new RegExp(W),X=new RegExp("^"+P+"$"),Y={ID:new RegExp("^#("+P+")"),CLASS:new RegExp("^\\.("+P+")"),TAG:new RegExp("^("+P+"|[*])"),ATTR:new RegExp("^"+H),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+be+"*(even|odd|(([+-]|)(\\d*)n|)"+be+"*(?:([+-]|)"+be+"*(\\d+)|))"+be+"*\\)|)","i"),bool:new RegExp("^(?:"+F+")$","i"),needsContext:new RegExp("^"+be+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+be+"*((?:-\\d)?\\d*)"+be+"*\\)|)(?=[^-]|$)","i")},U=/^(?:input|select|textarea|button)$/i,q=/^h\d$/i,ee=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,te=/[+~]/,ie=new RegExp("\\\\[\\da-fA-F]{1,6}"+be+"?|\\\\([^\\r\\n\\f])","g"),oe=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},ae=function(){c()},se=p(function(e){return!0===e.disabled&&r(e,"fieldset")},{dir:"parentNode",next:"legend"});try{R.apply(J=Q.call(Ie.childNodes),Ie.childNodes),J[Ie.childNodes.length].nodeType}catch(C){R={apply:function(e,t){we.apply(e,Q.call(t))},call:function(e){we.apply(e,Q.call(arguments,1))}}}for(C in t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if(c(e),S&&!D[n+" "]&&(!T||!T.test(n)))try{var i=E.call(e,n);if(i||le.disconnectedMatch||e.document&&11!==e.document.nodeType)return i}catch(e){D(n,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(ie,oe),e[3]=(e[3]||e[4]||e[5]||"").replace(ie,oe),"~="===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]||t.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]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return Y.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&K.test(n)&&(t=u(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(ie,oe).toLowerCase();return"*"===e?function(){return!0}:function(e){return r(e,t)}},CLASS:function(e){var t=O[e+" "];return t||(t=new RegExp("(^|"+be+")"+e+"("+be+"|$)"))&&O(e,function(e){return t.test("string"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,n,i){return function(o){var r=t.attr(o,e);return null==r?"!="===n:!n||(r+="","="===n?r===i:"!="===n?r!==i:"^="===n?i&&0===r.indexOf(i):"*="===n?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;ge.filter=function(e,t,n){var i=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===i.nodeType?ge.find.matchesSelector(i,e)?[i]:[]:ge.find.matches(e,ge.grep(t,function(e){return 1===e.nodeType}))},ge.fn.extend({find:function(e){var t,n,i=this.length,o=this;if("string"!=typeof e)return this.pushStack(ge(e).filter(function(){for(t=0;t)[^>]*|#([\w-]+))$/;(ge.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||Te,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:Ee.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 ge?t[0]:t,ge.merge(this,ge.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:ue,!0)),Se.test(i[1])&&ge.isPlainObject(t))for(i in t)ce(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=ue.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):ce(e)?void 0!==n.ready?n.ready(e):e(ge):ge.makeArray(e,this)}).prototype=ge.fn,Te=ge(ue);var Re=/^(?:parents|prev(?:Until|All))/,$e={children:!0,contents:!0,next:!0,prev:!0};ge.fn.extend({has:function(e){var t=ge(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,tt=/^$|^module$|\/(?:java|ecma)script/i;Je=ue.createDocumentFragment().appendChild(ue.createElement("div")),(qe=ue.createElement("input")).setAttribute("type","radio"),qe.setAttribute("checked","checked"),qe.setAttribute("name","t"),Je.appendChild(qe),le.checkClone=Je.cloneNode(!0).cloneNode(!0).lastChild.checked,Je.innerHTML="",le.noCloneChecked=!!Je.cloneNode(!0).lastChild.defaultValue,Je.innerHTML="",le.option=!!Je.lastChild;var nt={thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};nt.tbody=nt.tfoot=nt.colgroup=nt.caption=nt.thead,nt.th=nt.td,le.option||(nt.optgroup=nt.option=[1,""]);var it=/<|&#?\w+;/,ot=/^([^.]*)(?:\.(.+)|)/;ge.event={global:{},add:function(e,t,n,i,o){var r,a,s,l,c,d,u,h,p,f,g,m=Fe.get(e);if(Be(e))for(n.handler&&(n=(r=n).handler,o=r.selector),o&&ge.find.matchesSelector(ze,o),n.guid||(n.guid=ge.guid++),(l=m.events)||(l=m.events=Object.create(null)),(a=m.handle)||(a=m.handle=function(t){return void 0!==ge&&ge.event.triggered!==t.type?ge.event.dispatch.apply(e,arguments):void 0}),c=(t=(t||"").match(Le)||[""]).length;c--;)p=g=(s=ot.exec(t[c])||[])[1],f=(s[2]||"").split(".").sort(),p&&(u=ge.event.special[p]||{},p=(o?u.delegateType:u.bindType)||p,u=ge.event.special[p]||{},d=ge.extend({type:p,origType:g,data:i,handler:n,guid:n.guid,selector:o,needsContext:o&&ge.expr.match.needsContext.test(o),namespace:f.join(".")},r),(h=l[p])||((h=l[p]=[]).delegateCount=0,u.setup&&!1!==u.setup.call(e,i,f,a)||e.addEventListener&&e.addEventListener(p,a)),u.add&&(u.add.call(e,d),d.handler.guid||(d.handler.guid=n.guid)),o?h.splice(h.delegateCount++,0,d):h.push(d),ge.event.global[p]=!0)},remove:function(e,t,n,i,o){var r,a,s,l,c,d,u,h,p,f,g,m=Fe.hasData(e)&&Fe.get(e);if(m&&(l=m.events)){for(c=(t=(t||"").match(Le)||[""]).length;c--;)if(p=g=(s=ot.exec(t[c])||[])[1],f=(s[2]||"").split(".").sort(),p){for(u=ge.event.special[p]||{},h=l[p=(i?u.delegateType:u.bindType)||p]||[],s=s[2]&&new RegExp("(^|\\.)"+f.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=r=h.length;r--;)d=h[r],!o&&g!==d.origType||n&&n.guid!==d.guid||s&&!s.test(d.namespace)||i&&i!==d.selector&&("**"!==i||!d.selector)||(h.splice(r,1),d.selector&&h.delegateCount--,u.remove&&u.remove.call(e,d));a&&!h.length&&(u.teardown&&!1!==u.teardown.call(e,f,m.handle)||ge.removeEvent(e,p,m.handle),delete l[p])}else for(p in l)ge.event.remove(e,p+t[c],n,i,!0);ge.isEmptyObject(l)&&Fe.remove(e,"handle events")}},dispatch:function(e){var t,n,i,o,r,a,s=new Array(arguments.length),l=ge.event.fix(e),c=(Fe.get(this,"events")||Object.create(null))[l.type]||[],d=ge.event.special[l.type]||{};for(s[0]=l,t=1;t\s*$/g;ge.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var i,o,r,a,s,l,c,d=e.cloneNode(!0),u=Ke(e);if(!(le.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||ge.isXMLDoc(e)))for(a=b(d),i=0,o=(r=b(e)).length;i").attr(e.scriptAttrs||{}).prop({charset:e.scriptCharset,src:e.url}).on("load error",n=function(e){t.remove(),n=null,e&&o("error"===e.type?404:200,e.type)}),ue.head.appendChild(t[0])},abort:function(){n&&n()}}});var Jt,qt=[],Qt=/(=)\?(?=&|$)|\?\?/;ge.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=qt.pop()||ge.expando+"_"+$t.guid++;return this[e]=!0,e}}),ge.ajaxPrefilter("json jsonp",function(t,n,i){var o,r,a,s=!1!==t.jsonp&&(Qt.test(t.url)?"url":"string"==typeof t.data&&0===(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&Qt.test(t.data)&&"data");if(s||"jsonp"===t.dataTypes[0])return o=t.jsonpCallback=ce(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,s?t[s]=t[s].replace(Qt,"$1"+o):!1!==t.jsonp&&(t.url+=(Lt.test(t.url)?"&":"?")+t.jsonp+"="+o),t.converters["script json"]=function(){return a||ge.error(o+" was not called"),a[0]},t.dataTypes[0]="json",r=e[o],e[o]=function(){a=arguments},i.always(function(){void 0===r?ge(e).removeProp(o):e[o]=r,t[o]&&(t.jsonpCallback=n.jsonpCallback,qt.push(o)),a&&ce(r)&&r(a[0]),a=r=void 0}),"script"}),le.createHTMLDocument=((Jt=ue.implementation.createHTMLDocument("").body).innerHTML="
    ",2===Jt.childNodes.length),ge.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(le.createHTMLDocument?((i=(t=ue.implementation.createHTMLDocument("")).createElement("base")).href=ue.location.href,t.head.appendChild(i)):t=ue),r=!n&&[],(o=Se.exec(e))?[t.createElement(o[1])]:(o=x([e],t,r),r&&r.length&&ge(r).remove(),ge.merge([],o.childNodes)));var i,o,r},ge.fn.load=function(e,t,n){var i,o,r,a=this,s=e.indexOf(" ");return-1").append(ge.parseHTML(e)).find(i):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,r||[e.responseText,t,e])})}),this},ge.expr.pseudos.animated=function(e){return ge.grep(ge.timers,function(t){return e===t.elem}).length},ge.offset={setOffset:function(e,t,n){var i,o,r,a,s,l,c=ge.css(e,"position"),d=ge(e),u={};"static"===c&&(e.style.position="relative"),s=d.offset(),r=ge.css(e,"top"),l=ge.css(e,"left"),("absolute"===c||"fixed"===c)&&-1<(r+l).indexOf("auto")?(a=(i=d.position()).top,o=i.left):(a=parseFloat(r)||0,o=parseFloat(l)||0),ce(t)&&(t=t.call(e,n,ge.extend({},s))),null!=t.top&&(u.top=t.top-s.top+a),null!=t.left&&(u.left=t.left-s.left+o),"using"in t?t.using.call(e,u):d.css(u)}},ge.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){ge.offset.setOffset(this,e,t)});var t,n,i=this[0];return i?i.getClientRects().length?(t=i.getBoundingClientRect(),n=i.ownerDocument.defaultView,{top:t.top+n.pageYOffset,left:t.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,i=this[0],o={top:0,left:0};if("fixed"===ge.css(i,"position"))t=i.getBoundingClientRect();else{for(t=this.offset(),n=i.ownerDocument,e=i.offsetParent||n.documentElement;e&&(e===n.body||e===n.documentElement)&&"static"===ge.css(e,"position");)e=e.parentNode;e&&e!==i&&1===e.nodeType&&((o=ge(e).offset()).top+=ge.css(e,"borderTopWidth",!0),o.left+=ge.css(e,"borderLeftWidth",!0))}return{top:t.top-o.top-ge.css(i,"marginTop",!0),left:t.left-o.left-ge.css(i,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent;e&&"static"===ge.css(e,"position");)e=e.offsetParent;return e||ze})}}),ge.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n="pageYOffset"===t;ge.fn[e]=function(i){return Ne(this,function(e,i,o){var r;if(de(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===o)return r?r[t]:e[i];r?r.scrollTo(n?r.pageXOffset:o,n?o:r.pageYOffset):e[i]=o},e,i,arguments.length)}}),ge.each(["top","left"],function(e,t){ge.cssHooks[t]=j(le.pixelPosition,function(e,n){if(n)return n=L(e,t),lt.test(n)?ge(e).position()[t]+"px":n})}),ge.each({Height:"height",Width:"width"},function(e,t){ge.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,i){ge.fn[i]=function(o,r){var a=arguments.length&&(n||"boolean"!=typeof o),s=n||(!0===o||!0===r?"margin":"border");return Ne(this,function(t,n,o){var r;return de(t)?0===i.indexOf("outer")?t["inner"+e]:t.document.documentElement["client"+e]:9===t.nodeType?(r=t.documentElement,Math.max(t.body["scroll"+e],r["scroll"+e],t.body["offset"+e],r["offset"+e],r["client"+e])):void 0===o?ge.css(t,n,s):ge.style(t,n,o,s)},t,a?o:void 0,a)}})}),ge.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){ge.fn[t]=function(e){return this.on(t,e)}}),ge.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,i){return this.on(t,e,n,i)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.on("mouseenter",e).on("mouseleave",t||e)}}),ge.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,t){ge.fn[t]=function(e,n){return 0=2&&(!n[3]||-1!==n[3].indexOf("css=true"))&&layui.link(n[1]+"cascader"+(n[2]||"")+".css")}(window),layui.define(["jquery"],function(e){function t(e){e=e||window.event,e.stopPropagation?e.stopPropagation():e.cancelBubble=!0}function n(e,t,n,i){this.data=e,this.cascader=t,this.config=t.config,this.props=t.props,this.level=n,this.parentNode=i,this.icons=t.icons,this._checked=0,this._loading=!1,this.nodeId=t.data.nodeId++}function i(e){this.config=o.extend(!0,{elem:"",value:null,options:[],empty:"暂无数据",placeholder:"请选择",disabled:!1,clearable:!1,showAllLevels:!0,collapseTags:!1,minCollapseTagsNumber:1,separator:" / ",filterable:!1,filterMethod:function(e,t){return e.path.some(function(e){return-1!==e.label.indexOf(t)})},debounce:300,beforeFilter:function(e){return!0},popperClass:"",extendClass:!1,extendStyle:!1,disabledFixed:!1,maxSize:0,props:{strictMode:!1,expandTrigger:"click",multiple:!1,checkStrictly:!1,lazy:!1,lazyLoad:function(e,t){},value:"value",label:"label",children:"children",disabled:"disabled",leaf:"leaf"}},e),this.data={nodeId:1,nodes:[],menuData:[],activeNodeId:null,activeNode:null,checkedNodeIds:[],checkedNodes:[]},this.showPanel=!1,this.event={change:[],open:[],close:[]},this.filtering=!1,this._init(),this.closeEventId=0,this._maxSizeMode=null}var o=layui.jquery;n.prototype={ -constructor:n,get topParentNode(){return!this.parentNode&&this||this.topParentNode},childrenNode:void 0,get loading(){return this._loading},set loading(e){var t=this.$li;if(t){var n=this.icons.right,i=this.icons.loading,o=t.find("i");e?(o.addClass(i),o.removeClass(n)):(o.addClass(n),o.removeClass(i))}return this._loading=e},get label(){return this.data[this.props.label]},get value(){return this.data[this.props.value]},get disabled(){var e=this.props.multiple,t=this.config.maxSize,n=this.cascader.data.checkedNodeIds,i=this.props.disabled,o=this.props.checkStrictly;if(e&&0!==t&&n.length>=t&&-1===n.indexOf(this.nodeId)){if(o)return!0;if(!this.getAllLeafChildren().map(function(e){return e.nodeId}).some(function(e){return-1!==n.indexOf(e)}))return!0}return o?this.data[i]:this.path.some(function(e){return e.data[i]})},get children(){return this.data[this.props.children]},set children(e){this.data[this.props.children]=e},get leaf(){var e=this.data[this.props.leaf];return"boolean"==typeof e?e:!this.children||this.children.length<=0},get activeNodeId(){return this.cascader.data.activeNodeId},get checkedNodeIds(){return this.cascader.data.checkedNodeIds},get path(){var e=this.parentNode;return e?e.path.concat([this]):[this]},get isFiltering(){return this.cascader.isFiltering},get $tag(){var e=this.cascader,n=this.config.showAllLevels,i=this.config.disabled,o=this.disabled,r=this.config.disabledFixed,a=this.getPathLabel(n),s=e.get$tag(a,!(i||o&&r)),l=this;return s.find("i").click(function(n){t(n),l.selectedValue(),e.removeTag(l.value,l)}),s},getPathLabel:function(e){var t=this.path,n=this.config.separator;return e?t.map(function(e){return e.label}).join(n):t[t.length-1].label},init:function(){var e=this.props.multiple,t=this.props.checkStrictly,n=this.icons.from,i=this.icons.right,r="",a=this.label;this.leaf||(r=i),this.$li=o('
  • '),e||t?!e&&t?this._renderRadioCheckStrictly():e&&!t?this._renderMultiple():e&&t&&this._renderMultipleCheckStrictly():this._renderRadio()},initSuggestionLi:function(){var e=this.getPathLabel(!0);this.$suggestionLi=o('
  • '+e+"
  • "),this._renderFiltering()},bind:function(e){this.init(),e.append(this.$li)},bindSuggestion:function(e){this.initSuggestionLi(),e.append(this.$suggestionLi)},_renderFiltering:function(){var e=this.$suggestionLi,n=this.nodeId,i=this.icons.from,o=this.icons.ok,r=this,a=this.cascader,s=this.props.multiple,l='';e.click(function(i){t(i),r.selectedValue(),s?-1===r.checkedNodeIds.indexOf(n)?(e.removeClass("is-checked"),e.find(".el-icon-check").remove()):(e.addClass("is-checked"),e.append(l)):a.close()}),(s&&-1!==r.checkedNodeIds.indexOf(n)||!s&&r.activeNodeId===n)&&(e.addClass("is-checked"),e.append(l))},_renderRadio:function(){var e=this.$li,n=this.nodeId,i=this.icons.from,o=this.icons.ok,r=this.level,a=this.leaf,s=this,l=this.cascader,c=this.cascader.data.activeNode,d=this.parentNode;if(s.activeNodeId&&c.path.some(function(e){return e.nodeId===n})&&(s.activeNodeId===n&&e.prepend(''),e.addClass("is-active"),e.addClass("in-checked-path")),this.disabled)return void e.addClass("is-disabled");e.addClass("is-selectable"),d&&(d.$li.siblings().removeClass("in-active-path"),d.$li.addClass("in-active-path")),this._liClick(function(e){t(e);var n=s.childrenNode;a&&"click"===e.type&&(s.selectedValue(),l.close()),l._appendMenu(n,r+1,s)})},_renderRadioCheckStrictly:function(){var e=this.$li,n=this.nodeId,i=this.level,r=this.leaf,a=this,s=this.cascader,l=s.data.activeNode,c=this.parentNode;e.addClass("is-selectable");var d=o('');if(this.$radio=d,e.prepend(d),c&&(c.$li.siblings().removeClass("in-active-path"),c.$li.addClass("in-active-path")),this._liClick(function(e){t(e);var n=a.childrenNode;!a.disabled&&r&&"click"===e.type&&a.selectedValue(),s._appendMenu(n,i+1,a)}),a.activeNodeId&&l.path.some(function(e){return e.nodeId===n})&&(a.activeNodeId===n&&d.find(".el-radio__input").addClass("is-checked"),e.addClass("is-active"),e.addClass("in-checked-path")),this.disabled)return d.addClass("is-disabled"),void d.find(".el-radio__input").addClass("is-disabled");d.click(function(e){e.preventDefault(),!r&&a.selectedValue()})},_renderMultiple:function(){var e=this.$li,n=this.level,i=this.leaf,r=this,a=this.cascader,s=this._checked,l=this.parentNode;e.addClass("el-cascader-node");var c=o('');if(this.$checked=c,e.prepend(c),1===s?this.$checked.find(".el-checkbox__input").addClass("is-checked"):2===s&&this.$checked.find(".el-checkbox__input").addClass("is-indeterminate"),l&&(l.$li.siblings().removeClass("in-active-path"),l.$li.addClass("in-active-path")),this._liClick(function(e){t(e);var o=r.childrenNode;!r.disabled&&i&&"click"===e.type&&r.selectedValue(),a._appendMenu(o,n+1,r)}),this.disabled)return e.addClass("is-disabled"),c.addClass("is-disabled"),void c.find(".el-checkbox__input").addClass("is-disabled");c.click(function(e){if(e.preventDefault(),!i){var t=r.childrenNode;r.selectedValue(),a._appendMenu(t,n+1,r)}})},_renderMultipleCheckStrictly:function(){var e=this.$li,n=this.level,i=this.leaf,r=this,a=this.cascader,s=a.data.checkedNodeIds,l=a.data.checkedNodes,c=this.nodeId,d=this.parentNode;e.addClass("el-cascader-node is-selectable");var u=o('');if(this.$checked=u,e.prepend(u),l.some(function(e){return e.path.some(function(e){return e.nodeId===c})})&&(e.addClass("in-checked-path"),-1!==s.indexOf(c)&&this.$checked.find(".el-checkbox__input").addClass("is-checked")),d&&(d.$li.siblings().removeClass("in-active-path"),d.$li.addClass("in-active-path")),this._liClick(function(e){t(e);var o=r.childrenNode;!r.disabled&&i&&"click"===e.type&&r.selectedValue(),a._appendMenu(o,n+1,r)}),this.disabled)return u.addClass("is-disabled"),void u.find(".el-checkbox__input").addClass("is-disabled");u.click(function(e){if(e.preventDefault(),!i){r.selectedValue();var t=r.childrenNode;a._appendMenu(t,n+1,r)}})},transferParent:function(e,t,n){if(n||(n=this),this!==n||t){if(!1===(e&&e(this)))return}this.parentNode&&this.parentNode.transferParent(e,t,n)},transferChildren:function(e,t,n){if(n||(n=this),this!==n||t){if(!1===(e&&e(this)))return}var i=this.getChildren();if(i&&i.length>0)for(var o in i)i[o].transferChildren(e,t,n)},selectedValue:function(){var e=this.nodeId,t=this.cascader,n=this.props.multiple,i=this.props.checkStrictly,o=this.leaf;if(n||!o&&!i){if(n){var r,a=t.data.checkedNodeIds,s=t.data.checkedNodes,l=this.config.disabledFixed;if(i){var c=a.indexOf(e);-1===c?r=s.concat([this]):(r=s.concat(),r.splice(c,1))}else{var d=this.getAllLeafChildren();if(1===(1!==this._checked&&l?this._getMultipleChecked(d):this._checked))r=s.filter(function(e){return!d.some(function(t){return e.nodeId===t.nodeId})});else{var u=d.filter(function(e){return-1===a.indexOf(e.nodeId)});r=s.concat(u)}}var h=r.map(function(e){return e.nodeId});t._setCheckedValue(h,r)}}else t._setActiveValue(e,this)},_liLoad:function(e,t){var n=this.leaf,i=this.props.lazy,o=this.props.lazyLoad,r=this.children,a=this,s=this.cascader,l=this.level,c=this.props.multiple,d=this.props.checkStrictly;n||r&&0!==r.length||!i?t&&t(e):a.loading||(a.loading=!0,o(a,function(n){a.loading=!1,a.setChildren(s.initNodes(n,l+1,a)),a.children=n,t&&t(e),c&&!d&&a.transferParent(function(e){e.syncStyle()},!0)}))},_liClick:function(e){function t(t){o._liLoad(t,e)}var n=this.leaf,i=this.$li,o=this;("click"===this.props.expandTrigger||n)&&i.click(t),"hover"===this.props.expandTrigger&&i.mouseenter(t)},setChildren:function(e){this.childrenNode=e},getChildren:function(){return this.childrenNode},syncStyle:function(){var e=this.props.multiple,t=this.props.checkStrictly;e?t?this._sync.syncMultipleCheckStrictly(this):this._sync.syncMultiple(this):t?this._sync.syncRadioCheckStrictly(this):this._sync.syncRadio(this)},_sync:{syncRadio:function(e){var t=e.$li,n=e.icons.from,i=e.icons.ok,o=e.props.multiple,r=e.props.checkStrictly,a=e.nodeId;if(t&&!o&&!r){var s=e.cascader.data.activeNode;if(e.activeNodeId===a){0===t.find("."+i).length&&t.prepend('')}else t.find("."+i).remove();s&&s.path.some(function(e){return e.nodeId===a})?(t.addClass("is-active"),t.addClass("in-checked-path")):(t.removeClass("is-active"),t.removeClass("in-checked-path"))}},syncRadioCheckStrictly:function(e){var t=e.$li,n=e.props.checkStrictly,i=e.props.multiple;if(t&&!i&&n){var o=e.$radio,r=e.cascader.data.activeNode,a=e.nodeId;e.activeNodeId===a?o.find(".el-radio__input").addClass("is-checked"):o.find(".el-radio__input").removeClass("is-checked"),r&&r.path.some(function(e){return e.nodeId===a})?(t.addClass("is-active"),t.addClass("in-checked-path")):(t.removeClass("is-active"),t.removeClass("in-checked-path"))}},syncMultiple:function(e){var t=e.$li,n=e.props.checkStrictly,i=e.props.multiple,o=e.config.disabledFixed;if(i&&!n){var r=e.getAllLeafChildren(o),a=e._getMultipleChecked(r);if(e._checked=a,t){var s=e.$checked.find(".el-checkbox__input");0===a?(s.removeClass("is-checked"),s.removeClass("is-indeterminate")):1===a?(s.removeClass("is-indeterminate"),s.addClass("is-checked")):2===a&&(s.removeClass("is-checked"),s.addClass("is-indeterminate"))}}},syncMultipleCheckStrictly:function(e){var t=e.$li,n=e.props.checkStrictly,i=e.props.multiple;if(t&&i&&n){var o=e.cascader.data.checkedNodes,r=e.checkedNodeIds,a=e.nodeId,s=o.some(function(e){return e.path.some(function(e){return e.nodeId===a})}),l=e.$checked.find(".el-checkbox__input");r.some(function(e){return e===a})?l.addClass("is-checked"):l.removeClass("is-checked"),s?t.addClass("in-checked-path"):t.removeClass("in-checked-path")}}},getAllLeafChildren:function(e){if(this.leaf)return[this];var t=[];return this.transferChildren(function(n){if(n.disabled&&!e)return!1;n.leaf&&t.push(n)}),t},expandPanel:function(){var e=this.path,t=this.cascader;e.forEach(function(e,n,i){if(n!==i.length-1){var o=e.childrenNode;t._appendMenu(o,e.level+1,e.parentNode)}})},_getMultipleChecked:function(e){for(var t=this.cascader,n=t.data.checkedNodeIds,i=e.map(function(e){return e.nodeId}),o=0,r=0;r0&&1!==o?2:1:1===o?2:0}return o}},i.prototype={constructor:i,get props(){return this.config.props},get isFiltering(){return this.filtering},set isFiltering(e){if(this.filtering!==e){this.filtering=!!e;var t=this.$panel;this.filtering?(t.find(".el-cascader-panel").hide(),t.find(".el-cascader__suggestion-panel").show()):(t.find(".el-cascader-panel").show(),t.find(".el-cascader__suggestion-panel").hide(),this.$tagsInput&&this.$tagsInput.val(""))}},set maxSizeMode(e){this._maxSizeMode!==e&&(this._maxSizeMode=e,this.refreshMenu())},icons:{from:"layui-icon",down:"layui-icon-down",close:"layui-icon-close",right:"layui-icon-right",ok:"layui-icon-ok",loading:"layui-icon-loading-1 layui-anim layui-anim-rotate layui-anim-loop"},_init:function(){this._checkConfig(),this._initInput(),this._initPanel(),this.setOptions(this.config.options);var e=this;o(window).scroll(function(){e._resetXY()}),o(window).resize(function(){e._resetXY()}),this.$div.click(function(t){if(!e.config.disabled){e.showPanel?e.close():e.open()}})},_checkConfig:function(){var e=this.config.elem;if(!e||0===o(e).length)throw new Error("缺少elem节点选择器");var t=this.config.maxSize;if("number"!=typeof t||t<0)throw new Error("maxSize应是一个大于等于0的有效的number值");if(!Array.isArray(this.config.options))throw new Error("options不是一个有效的数组")},_initRoot:function(){var e=this.props.lazy,t=this.props.lazyLoad,n=this,i=this.data.nodes;i.length>0||!e?this._appendMenu(i,0):e&&(this._appendMenu(i,0),t({root:!0,level:0},function(e){n.data.nodes=n.initNodes(e,0,null),n._appendMenu(n.data.nodes,0)}))},setOptions:function(e){this.config.options=e,this.data.nodes=this.initNodes(e,0,null),this._initRoot(),this.setValue(this.config.value)},_resetXY:function(){var e=this.$div,t=e.offset(),n=this.$panel;if(n){var i=window.innerHeight,o=window.innerWidth,r=n.height(),a=n.width(),s=e.height(),l=e[0].getBoundingClientRect(),c=n.find(".popper__arrow"),d=Math.min(o-l.x-a-5,0);if(i-(l.top+s)r+20)n.attr("x-placement","top-start"),n.css({top:t.top-20-r+"px",left:t.left+d+"px"});else{n.attr("x-placement","bottom-start");var u=Math.max(r-(i-l.y-s-15),0);n.css({top:t.top+s-u+"px",left:t.left+d+"px"})}c.css("left",35-d+"px")}},get $menus(){return this.$panel&&this.$panel.find(".el-cascader-panel .el-cascader-menu")},_initInput:function(){var e=o(this.config.elem),t=this;null===this.config.value&&e.attr("value")&&(this.config.value=e.attr("value"));var n=this.config.placeholder,i=this.icons.from,r=this.icons.down,a=this.props.multiple,s=this.config.extendClass,l=this.config.extendStyle;if(this.$div=o('
    '),l){var c=e.attr("style");c&&this.$div.attr("style",c)}if(s){var d=e.attr("class");d&&d.split(" ").forEach(function(e){t.$div.addClass(e)})}this.$input=o('
    '),this.$div.append(this.$input),this.$inputRow=this.$input.find(".el-input__inner"),a&&(this.$tags=o('
    \x3c!----\x3e
    '),this.$div.append(this.$tags)),this._initHideElement(e).after(this.$div),this.$icon=this.$input.find("i"),this._initFilterableInputEvent(),this.disabled(this.config.disabled)},_initHideElement:function(e){if("input"===e.prop("tagName").toLowerCase())return e.hide(),e.attr("type","hidden"),this.$ec=e,e;var t=e[0].attributes,n=o(""),i=Object.keys(t);for(var r in i){var a=t[r];n.attr(a.name,a.value)}return n.hide(),n.attr("type","hidden"),this.$ec=n,e.before(n),e.remove(),n},_initFilterableInputEvent:function(){function e(e){var t=this;n&&clearTimeout(n),n=setTimeout(function(){n=null;var e=o(t).val();if(!e)return void(d.isFiltering=!1);if(d.open(),"function"==typeof s&&s(e)){d.isFiltering=!0;var i=d.getNodes(),r=i.filter(function(t){var n;return n=c?t.disabled:t.path.some(function(e){return e.disabled}),!(!t.leaf&&!c||n||"function"!=typeof l||!l(t,e))});d._setSuggestionMenu(r)}},r)}if(this.config.filterable){var n,i=this.props.multiple,r=this.config.debounce,a=this.config.placeholder,s=this.config.beforeFilter,l=this.config.filterMethod,c=this.props.checkStrictly,d=this;if(i){this.$tagsInput=o('');var u=this.$tagsInput;this.$tags.append(u),u.on("keydown",e),u.click(function(e){d.isFiltering&&t(e)})}else{var h=this.$inputRow;h.removeAttr("readonly"),h.on("keydown",e),h.click(function(e){d.isFiltering&&t(e)})}}},_initPanel:function(){var e=this.$panel,n=this.config.popperClass||"";e||(this.$panel=o(''),e=this.$panel,e.appendTo("body"),e.click(function(e){t(e)}),this._initSuggestionPanel())},_appendMenu:function(e,t,n,i){if(this._removeMenu(t),!n||!n.leaf){var r=this.data.menuData,a=o('');this.$panel.find(".el-cascader-panel").append(a),this._appendLi(a,e);var s={nodes:e,level:t,parentNode:n,scrollbar:{top:0,left:0}};i&&(s.scrollbar=i.scrollbar),this._initScrollbar(a,s),this._resetXY(),r.push(s)}},_removeMenu:function(e){var t=e-1;-1!==t?this.$panel.find(".el-cascader-panel .el-cascader-menu:gt("+t+")").remove():this.$panel.find(".el-cascader-panel .el-cascader-menu").remove();var n=this.data.menuData;n.length>e&&n.splice(e,n.length-e)},_appendLi:function(e,t){var n=e.find(".el-cascader-menu__list");if(!t||0===t.length){var i=this.config.empty;return void n.append('
    '+i+"
    ")}o.each(t,function(e,t){t.bind(n)})},refreshMenu:function(){var e=this;this.data.menuData.concat([]).forEach(function(t){e._appendMenu(t.nodes,t.level,t.parentNode,t)})},_initSuggestionPanel:function(){if(this.config.filterable){var e=this.$suggestionPanel;e||(this.$suggestionPanel=o(''),e=this.$suggestionPanel,this.$panel.find(".popper__arrow").before(e),e.click(function(e){t(e)}))}},_setSuggestionMenu:function(e){var t=this.$suggestionPanel,n=t.find(".el-cascader__suggestion-list");if(n.empty(),t.find(".el-scrollbar__bar").remove(),!e||0===e.length)return void n.append('
  • 无匹配数据
  • ');o.each(e,function(e,t){t.bindSuggestion(n)}),this._initScrollbar(t,{scrollbar:{top:0,left:0}}),this._resetXY()},initNodes:function(e,t,i){var o=[];for(var r in e){var a=e[r],s=new n(a,this,t,i);null!==s.value&&void 0!==s.value&&(o.push(s),s.children&&s.children.length>0&&s.setChildren(this.initNodes(s.children,t+1,s)))}return o},_setActiveValue:function(e,t){if(this.data.activeNodeId!==e){var n=this.data.activeNode;this.data.activeNodeId=e,this.data.activeNode=t,n&&n.transferParent(function(e){e.syncStyle()},!0),t&&t.transferParent(function(e){e.syncStyle()},!0),this.change(t&&t.value,t),null!==e&&this._setClear()}},_setCheckedValue:function(e,t){var n,i=this.data.checkedNodes,o=this.config.maxSize;e.length>0&&0!==o&&e.length>=o?(e=e.slice(0,o),t=t.slice(0,o),n=!0):n=!1,this.data.checkedNodeIds=e||[],this.data.checkedNodes=t||[];var r=[],a=[];i.forEach(function(e){e.path.forEach(function(e){-1===a.indexOf(e.nodeId)&&(r.push(e),a.push(e.nodeId))})}),t.forEach(function(e){e.path.forEach(function(e){-1===a.indexOf(e.nodeId)&&(r.push(e),a.push(e.nodeId))})}),r.forEach(function(e){e.syncStyle()}),this.change(t.map(function(e){return e.value}),t),this._setClear(),this.maxSizeMode=n},setValue:function(e){if((this.data.activeNodeId||this.data.checkedNodeIds.length>0)&&this.clearCheckedNodes(),e){var t=this.props.strictMode;if(t&&!Array.isArray(e))throw new Error("严格模式下,value必须是一个包含父子节点数组结构.");var n=this.getNodes(this.data.nodes),i=this.props.checkStrictly,o=this.props.multiple,r=this.config.disabledFixed;if(o){var a=n.filter(function(n){return!(!i&&!n.leaf||n.disabled&&!r)&&(t?e.some(function(e){if(!Array.isArray(e))throw new Error("多选严格模式下,value必须是一个二维数组结构.");var t=n.path;return e.length===t.length&&e.every(function(e,n){return t[n].value===e})}):-1!==e.indexOf(n.value))}),s=a.map(function(e){return e.nodeId});if(this._setCheckedValue(s,a),a.length>0){a[0].expandPanel()}}else for(var l=0;l
    ');e.append(r);var a=o(r[1]).find(".el-scrollbar__thumb"),s=o(r[0]).find(".el-scrollbar__thumb"),l=e.find(".el-scrollbar__wrap"),c=this.$panel,d=e.find("li"),u=Math.max(c.height(),e.height()),h=(u-6)/(d.height()*d.length),p=c.width()/d.width();a.mousedown(function(e){e.stopImmediatePropagation(),t(e);var n=function(){return!1};o(document).bind("selectstart",n);var i=e.clientY,r=l.scrollTop(),a=function(e){e.stopImmediatePropagation();var t=r+(e.clientY-i)/h;l.scrollTop(t)};o(document).bind("mousemove",a),o(document).one("mouseup",function(e){t(e),e.stopImmediatePropagation(),o(document).off("mousemove",a),o(document).off("selectstart",n)})}),l.scroll(function(){var e=o(this);n.scrollbar.top=e.scrollTop(),n.scrollbar.left=e.scrollLeft(),i(n.scrollbar.top,n.scrollbar.left)}),l.scrollTop(n.scrollbar.top),i(n.scrollbar.top,n.scrollbar.left)},_fillingPath:function(){var e=this.props.multiple,t=this.config.showAllLevels,n=this.config.separator,i=this.config.collapseTags,o=this.$inputRow,r=this.config.placeholder,a=this;if(e){this.$tags.find(".el-tag").remove();var s=this.$tagsInput;o.css("height","");var l=this.data.checkedNodes,c=Math.max(this.config.minCollapseTagsNumber,1);if(l.length>0){var d=[],u=l;i&&(u=l.slice(0,Math.min(l.length,c))),u.forEach(function(e){d.push(e.$tag)}),i&&l.length>c&&d.push(a.get$tag("+ "+(l.length-c),!1)),d.forEach(function(e){s?s.before(e):a.$tags.append(e)})}var h=a.$tags.height();h>o.height()&&o.css("height",h+4+"px"),this._resetXY(),l.length>0?(o.removeAttr("placeholder"),s&&s.removeAttr("placeholder",r)):(o.attr("placeholder",r),s&&s.attr("placeholder",r))}else{var p=this.data.activeNode,f=p&&p.path||[];t?this._$inputRowSetValue(f.map(function(e){return e.label}).join(n)):this._$inputRowSetValue(p&&p.label||"")}},_$inputRowSetValue:function(e){e=e||"";var t=this.$inputRow;t.attr("value",e),t.val(e)},get$tag:function(e,t){var n=this.icons.from,i=this.icons.close;return o(''+e+""+(t?'':"")+"")},_setClear:function(){function e(){i.$icon.removeClass(i.icons.down),i.$icon.addClass(i.icons.close)}function n(){i.$icon.removeClass(i.icons.close),i.$icon.addClass(i.icons.down)}var i=this;i.$div.mouseenter(function(){e()}),i.$div.mouseleave(function(){n()}),i.$icon.off("click");var o,r=this.props.multiple;o=r?this.data.checkedNodeIds.length>0:!!this.data.activeNodeId,o&&!this.config.disabled&&this.config.clearable?i.$icon.one("click",function(e){t(e),i.close(),i.clearCheckedNodes(),n(),i.$icon.off("mouseenter"),i.$div.off("mouseenter"),i.$div.off("mouseleave")}):(n(),i.$icon.off("mouseenter"),i.$div.off("mouseenter"),i.$div.off("mouseleave"))},disabled:function(e){this.config.disabled=!!e,this.config.disabled?(this.$div.addClass("is-disabled"),this.$div.find(".el-input--suffix").addClass("is-disabled"),this.$inputRow.attr("disabled","disabled"),this.$tagsInput&&this.$tagsInput.attr("disabled","disabled").hide()):(this.$div.removeClass("is-disabled"),this.$div.find(".el-input--suffix").removeClass("is-disabled"),this.$inputRow.removeAttr("disabled"),this.$tagsInput&&this.$tagsInput.removeAttr("disabled").show()),this._setClear(),this._fillingPath()},change:function(e,t){this.props.multiple?e&&e.length>0?this.$ec.attr("value",JSON.stringify(e)):this.$ec.removeAttr("value"):this.$ec.attr("value",e||""),this._fillingPath(),this.event.change.forEach(function(n){"function"==typeof n&&n(e,t)})},close:function(e){if(this.showPanel&&(!e||this.closeEventId===e)){this.showPanel=!1,this.$div.find(".layui-icon-down").removeClass("is-reverse"),this.$panel.slideUp(100),this.visibleChange(!1),this.$input.removeClass("is-focus");this.config.filterable&&(this.isFiltering=!1,this._fillingPath()),this.event.close.forEach(function(e){"function"==typeof e&&e()})}},open:function(){if(!this.showPanel){this.showPanel=!0,this.closeEventId++;var e=this;setTimeout(function(){o(document).one("click",e.close.bind(e,e.closeEventId))}),this._resetXY(),this.$div.find(".layui-icon-down").addClass("is-reverse"),this.$panel.slideDown(200),this.visibleChange(!0),this.$input.addClass("is-focus"),this.event.open.forEach(function(e){"function"==typeof e&&e()})}},visibleChange:function(e){},removeTag:function(e,t){},getCheckedValues:function(){var e=this.props.strictMode;if(this.props.multiple){var t=this.data.checkedNodes;return e?t.map(function(e){return e.path.map(function(e){return e.value})}):t.map(function(e){return e.value})}var n=this.data.activeNode;return e?n&&n.path.map(function(e){return e.value}):n&&n.value},getCheckedNodes:function(){var e=this.props.strictMode;if(this.props.multiple){var t=this.data.checkedNodes;return e?t&&t.map(function(e){return e.path}):t}var n=this.data.activeNode;return e?n&&n.path:n},clearCheckedNodes:function(e){if(this.props.multiple){var t=this.config.disabledFixed;if(!e&&t){var n=this.data.checkedNodes,i=n.filter(function(e){return e.disabled}),o=i.map(function(e){return e.nodeId});this._setCheckedValue(o,i)}else this._setCheckedValue([],[])}else this._setActiveValue(null,null)}};var r=function(){var e=this;return{setOptions:function(t){e.setOptions(t)},setValue:function(t){e.setValue(t)},changeEvent:function(t){e.event.change.push(t)},closeEvent:function(t){e.event.close.push(t)},openEvent:function(t){e.event.open.push(t)},disabled:function(t){e.disabled(t)},close:function(){e.close()},open:function(){e.open()},getCheckedNodes:function(){return e.getCheckedNodes()},getCheckedValues:function(){return e.getCheckedValues()},clearCheckedNodes:function(t){e.clearCheckedNodes(t)},expandNode:function(t){for(var n=e.getNodes(e.data.nodes),i=0;ic.top&&(o=!1),i?0:l.top-s.top);o?(i=t.offsetHeight,this.dom.menu.style.left="0",this.dom.menu.style.top=n+i+"px",this.dom.menu.style.bottom=""):(this.dom.menu.style.left="0",this.dom.menu.style.top="",this.dom.menu.style.bottom="0px"),this.limitHeight&&(s=o?c.bottom-l.bottom-10:l.top-c.top-10,this.dom.list.style.maxHeight=s+"px",this.dom.list.style.overflowY="auto"),this.dom.absoluteAnchor.appendChild(this.dom.root),this.selection=(0,a.getSelection)(),this.anchor=t,setTimeout(function(){d.dom.focusButton.focus()},0), -e.visibleMenu&&e.visibleMenu.hide(),e.visibleMenu=this}},{key:"hide",value:function(){this.dom.absoluteAnchor&&(this.dom.absoluteAnchor.destroy(),delete this.dom.absoluteAnchor),this.dom.root.parentNode&&(this.dom.root.parentNode.removeChild(this.dom.root),this.onClose)&&this.onClose(),e.visibleMenu===this&&(e.visibleMenu=void 0)}},{key:"_onExpandItem",value:function(e){var t,n=this,i=e===this.expandedItem,o=this.expandedItem;o&&(o.ul.style.height="0",o.ul.style.padding="",setTimeout(function(){n.expandedItem!==o&&(o.ul.style.display="",a.removeClassName)(o.ul.parentNode,"jsoneditor-selected")},300),this.expandedItem=void 0),i||((t=e.ul).style.display="block",t.clientHeight,setTimeout(function(){if(n.expandedItem===e){for(var i=0,o=0;o/gi,"\n"))),a.appendChild(s),r.appendChild(a)),r.onclick=function(){o.onFocusLine(i)},n.appendChild(r)}),this.dom.validationErrors=r,this.dom.validationErrorsContainer.appendChild(r),this.dom.additionalErrorsIndication.title=e.length+" errors total",this.dom.validationErrorsContainer.clientHeighte[0].length)||(e=t,n=r,this.options.flex));r++);return e?((i=e[0].match(/\n.*/g))&&(this.yylineno+=i.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:i?i[i.length-1].length-1:this.yylloc.last_column+e[0].length},this.yytext+=e[0],this.match+=e[0],this.yyleng=this.yytext.length,this._more=!1,this._input=this._input.slice(e[0].length),this.matched+=e[0],i=this.performAction.call(this,this.yy,this,o[n],this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),i||void 0):""===this._input?this.EOF:void this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var e=this.next();return void 0!==e?e:this.lex()},begin:function(e){this.conditionStack.push(e)},popState:function(){return this.conditionStack.pop()},_currentRules:function(){return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules},topState:function(){return this.conditionStack[this.conditionStack.length-2]},pushState:function(e){this.begin(e)},options:{},performAction:function(e,t,n,i){switch(n){case 0:break;case 1:return 6;case 2:return t.yytext=t.yytext.substr(1,t.yyleng-2),4;case 3:return 17;case 4:return 18;case 5:return 23;case 6:return 24;case 7:return 22;case 8:return 21;case 9:return 10;case 10:return 11;case 11:return 8;case 12:return 14;case 13:return"INVALID"}},rules:[/^(?:\s+)/,/^(?:(-?([0-9]|[1-9][0-9]+))(\.[0-9]+)?([eE][-+]?[0-9]+)?\b)/,/^(?:"(?:\\[\\"bfnrt/]|\\u[a-fA-F0-9]{4}|[^\\\0-\x09\x0a-\x1f"])*")/,/^(?:\{)/,/^(?:\})/,/^(?:\[)/,/^(?:\])/,/^(?:,)/,/^(?::)/,/^(?:true\b)/,/^(?:false\b)/,/^(?:null\b)/,/^(?:$)/,/^(?:.)/],conditions:{INITIAL:{rules:[0,1,2,3,4,5,6,7,8,9,10,11,12,13],inclusive:!0}}};var n;t.parser=n,t.parse=n.parse.bind(n)},4772:function(e){"use strict";function t(){}function n(e,t){return e.hasOwnProperty(t)&&(!0===e[t]||e[t].length)}function i(e,t,n){e.parentNode?e.parentNode.parentNode||t.appendChild(e.parentNode):t.appendChild(e),c.removeClass(e,"excluded"),n||(e.textContent=e.textContent)}function o(){var e,t;this.items.length&&(e=document.createDocumentFragment(),this.config.pagination?(t=this.pages.slice(0,this.pageIndex),c.each(t,function(t,n){c.each(n,function(t,n){i(n,e,this.customOption)},this)},this)):c.each(this.items,function(t,n){i(n,e,this.customOption)},this),e.childElementCount&&(c.removeClass(this.items[this.navIndex],"active"),this.navIndex=(e.querySelector(".selectr-option.selected")||e.querySelector(".selectr-option")).idx,c.addClass(this.items[this.navIndex],"active")),this.tree.appendChild(e))}function r(e,t){t=t||e;var n={class:"selectr-option",role:"treeitem","aria-selected":!1};return this.customOption?n.html=this.config.renderOption(t):n.textContent=e.textContent,(t=c.createElement("li",n)).idx=e.idx,this.items.push(t),e.defaultSelected&&this.defaultSelected.push(e.idx),e.disabled&&(t.disabled=!0,c.addClass(t,"disabled")),t}function a(){var e,t=this.tree,n=t.scrollTop;t.scrollHeight-t.offsetHeight<=n&&this.pageIndex"+t.label+""}),c.each(t.children,function(e,t){t.idx=u,d.appendChild(r.call(this,t,d)),u++},this)):(t.idx=u,r.call(this,t),u++)},this),this.config.data&&Array.isArray(this.config.data)&&(a=!(this.data=[]),d=!1,u=0,c.each(this.config.data,function(e,t){n(t,"children")?(a=c.createElement("optgroup",{label:t.text}),d=c.createElement("ul",{class:"selectr-optgroup",role:"group",html:"
  • "+t.text+"
  • "}),c.each(t.children,function(e,t){(s=new Option(t.text,t.value,!1,t.hasOwnProperty("selected")&&!0===t.selected)).disabled=n(t,"disabled"),this.options.push(s),a.appendChild(s),s.idx=u,d.appendChild(r.call(this,s,t)),this.data[u]=t,u++},this),this.el.appendChild(a)):((s=new Option(t.text,t.value,!1,t.hasOwnProperty("selected")&&!0===t.selected)).disabled=n(t,"disabled"),this.options.push(s),s.idx=u,r.call(this,s,t),this.data[u]=t,u++)},this)),this.setSelected(!0);for(var h=this.navIndex=0;hthis.tree.lastElementChild.idx){this.navIndex=this.tree.lastElementChild.idx;break}if(this.navIndexthis.optsRect.top+this.optsRect.height&&(this.tree.scrollTop=this.tree.scrollTop+(e.top+e.height-(this.optsRect.top+this.optsRect.height))),this.navIndex===this.tree.childElementCount-1&&this.requiresPagination&&a.call(this)):0===this.navIndex?this.tree.scrollTop=0:e.top-this.optsRect.top<0&&(this.tree.scrollTop=this.tree.scrollTop+(e.top-this.optsRect.top)),n&&c.removeClass(n,"active"),c.addClass(this.items[this.navIndex],"active")}else this.navigating=!1}.bind(this),this.events.reset=this.reset.bind(this),(this.config.nativeDropdown||this.mobileDevice)&&(this.container.addEventListener("touchstart",function(e){e.changedTouches[0].target===t.el&&t.toggle()},!!this.supportsEventPassiveOption&&{passive:!0}),this.container.addEventListener("click",function(e){e.target===t.el&&t.toggle()}),this.el.addEventListener("change",function(e){e.__selfTriggered||(t.el.multiple?(e=t.getSelectedProperties("idx"),e=function(e,t){for(var n,i=[],o=e.slice(0),r=0;rn?(c.addClass(this.container,"inverted"),this.isInverted=!0):(c.removeClass(this.container,"inverted"),this.isInverted=!1),this.optsRect=c.rect(this.tree)},l.prototype.getOptionByIndex=function(e){return this.options[e]},l.prototype.getOptionByValue=function(e){for(var t=!1,n=0,i=this.options.length;nx.cacheLength&&delete t[e.shift()],t[n+" "]=i}}function i(e){return e[$]=!0,e}function o(e){var t=k.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function a(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&&se(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function s(e){return i(function(t){return t=+t,i(function(n,i){for(var o,r=e([],n.length,t),a=r.length;a--;)n[o=r[a]]&&(n[o]=!(i[o]=n[o]))})})}function l(e){return e&&void 0!==e.getElementsByTagName&&e}function c(e){var n,i=e?e.ownerDocument||e:Ie;return i!=k&&9===i.nodeType&&i.documentElement&&(_=(k=i).documentElement,S=!ge.isXMLDoc(k),E=_.matches||_.webkitMatchesSelector||_.msMatchesSelector,_.msMatchesSelector&&Ie!=k&&(n=k.defaultView)&&n.top!==n&&n.addEventListener("unload",ae),le.getById=o(function(e){return _.appendChild(e).id=ge.expando,!k.getElementsByName||!k.getElementsByName(ge.expando).length}),le.disconnectedMatch=o(function(e){return E.call(e,"*")}),le.scope=o(function(){return k.querySelectorAll(":scope")}),le.cssHas=o(function(){try{return k.querySelector(":has(*,:jqfake)"),!1}catch(e){return!0}}),le.getById?(x.filter.ID=function(e){var t=e.replace(ie,oe);return function(e){return e.getAttribute("id")===t}},x.find.ID=function(e,t){if(void 0!==t.getElementById&&S){var n=t.getElementById(e);return n?[n]:[]}}):(x.filter.ID=function(e){var t=e.replace(ie,oe);return function(e){var n=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},x.find.ID=function(e,t){if(void 0!==t.getElementById&&S){var n,i,o,r=t.getElementById(e);if(r){if((n=r.getAttributeNode("id"))&&n.value===e)return[r];for(o=t.getElementsByName(e),i=0;r=o[i++];)if((n=r.getAttributeNode("id"))&&n.value===e)return[r]}return[]}}),x.find.TAG=function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):t.querySelectorAll(e)},x.find.CLASS=function(e,t){if(void 0!==t.getElementsByClassName&&S)return t.getElementsByClassName(e)},T=[],o(function(e){var t;_.appendChild(e).innerHTML="",e.querySelectorAll("[selected]").length||T.push("\\["+be+"*(?:value|"+B+")"),e.querySelectorAll("[id~="+$+"-]").length||T.push("~="),e.querySelectorAll("a#"+$+"+*").length||T.push(".#.+[+~]"),e.querySelectorAll(":checked").length||T.push(":checked"),(t=k.createElement("input")).setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),_.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&T.push(":enabled",":disabled"),(t=k.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||T.push("\\["+be+"*name"+be+"*="+be+"*(?:''|\"\")")}),le.cssHas||T.push(":has"),T=T.length&&new RegExp(T.join("|")),F=function(e,n){if(e===n)return A=!0,0;var i=!e.compareDocumentPosition-!n.compareDocumentPosition;return i||(1&(i=(e.ownerDocument||e)==(n.ownerDocument||n)?e.compareDocumentPosition(n):1)||!le.sortDetached&&n.compareDocumentPosition(e)===i?e===k||e.ownerDocument==Ie&&t.contains(Ie,e)?-1:n===k||n.ownerDocument==Ie&&t.contains(Ie,n)?1:w?ne.call(w,e)-ne.call(w,n):0:4&i?-1:1)}),k}function d(){}function u(e,n){var i,o,r,a,s,l,c,d=N[e+" "];if(d)return n?0:d.slice(0);for(s=e,l=[],c=x.preFilter;s;){for(a in i&&!(o=G.exec(s))||(o&&(s=s.slice(o[0].length)||s),l.push(r=[])),i=!1,(o=Z.exec(s))&&(i=o.shift(),r.push({value:i,type:o[0].replace(Ce," ")}),s=s.slice(i.length)),x.filter)!(o=Y[a].exec(s))||c[a]&&!(o=c[a](o))||(i=o.shift(),r.push({value:i,type:a,matches:o}),s=s.slice(i.length));if(!i)break}return n?s.length:s?t.error(e):N(e,l).slice(0)}function h(e){for(var t=0,n=e.length,i="";t+~]|"+be+")"+be+"*"),z=new RegExp(be+"|>"),K=new RegExp(W),X=new RegExp("^"+P+"$"),Y={ID:new RegExp("^#("+P+")"),CLASS:new RegExp("^\\.("+P+")"),TAG:new RegExp("^("+P+"|[*])"),ATTR:new RegExp("^"+H),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+be+"*(even|odd|(([+-]|)(\\d*)n|)"+be+"*(?:([+-]|)"+be+"*(\\d+)|))"+be+"*\\)|)","i"),bool:new RegExp("^(?:"+B+")$","i"),needsContext:new RegExp("^"+be+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+be+"*((?:-\\d)?\\d*)"+be+"*\\)|)(?=[^-]|$)","i")},J=/^(?:input|select|textarea|button)$/i,q=/^h\d$/i,ee=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,te=/[+~]/,ie=new RegExp("\\\\[\\da-fA-F]{1,6}"+be+"?|\\\\([^\\r\\n\\f])","g"),oe=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},ae=function(){c()},se=p(function(e){return!0===e.disabled&&r(e,"fieldset")},{dir:"parentNode",next:"legend"});try{R.apply(U=Q.call(Ie.childNodes),Ie.childNodes),U[Ie.childNodes.length].nodeType}catch(C){R={apply:function(e,t){we.apply(e,Q.call(t))},call:function(e){we.apply(e,Q.call(arguments,1))}}}for(C in t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if(c(e),S&&!D[n+" "]&&(!T||!T.test(n)))try{var i=E.call(e,n);if(i||le.disconnectedMatch||e.document&&11!==e.document.nodeType)return i}catch(e){D(n,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(ie,oe),e[3]=(e[3]||e[4]||e[5]||"").replace(ie,oe),"~="===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]||t.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]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return Y.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&K.test(n)&&(t=u(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(ie,oe).toLowerCase();return"*"===e?function(){return!0}:function(e){return r(e,t)}},CLASS:function(e){var t=j[e+" "];return t||(t=new RegExp("(^|"+be+")"+e+"("+be+"|$)"))&&j(e,function(e){return t.test("string"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,n,i){return function(o){var r=t.attr(o,e);return null==r?"!="===n:!n||(r+="","="===n?r===i:"!="===n?r!==i:"^="===n?i&&0===r.indexOf(i):"*="===n?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;ge.filter=function(e,t,n){var i=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===i.nodeType?ge.find.matchesSelector(i,e)?[i]:[]:ge.find.matches(e,ge.grep(t,function(e){return 1===e.nodeType}))},ge.fn.extend({find:function(e){var t,n,i=this.length,o=this;if("string"!=typeof e)return this.pushStack(ge(e).filter(function(){for(t=0;t)[^>]*|#([\w-]+))$/;(ge.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||Te,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:Ee.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 ge?t[0]:t,ge.merge(this,ge.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:ue,!0)),Se.test(i[1])&&ge.isPlainObject(t))for(i in t)ce(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=ue.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):ce(e)?void 0!==n.ready?n.ready(e):e(ge):ge.makeArray(e,this)}).prototype=ge.fn,Te=ge(ue);var Re=/^(?:parents|prev(?:Until|All))/,$e={children:!0,contents:!0,next:!0,prev:!0};ge.fn.extend({has:function(e){var t=ge(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,tt=/^$|^module$|\/(?:java|ecma)script/i;Ue=ue.createDocumentFragment().appendChild(ue.createElement("div")),(qe=ue.createElement("input")).setAttribute("type","radio"),qe.setAttribute("checked","checked"),qe.setAttribute("name","t"),Ue.appendChild(qe),le.checkClone=Ue.cloneNode(!0).cloneNode(!0).lastChild.checked,Ue.innerHTML="",le.noCloneChecked=!!Ue.cloneNode(!0).lastChild.defaultValue,Ue.innerHTML="",le.option=!!Ue.lastChild;var nt={thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};nt.tbody=nt.tfoot=nt.colgroup=nt.caption=nt.thead,nt.th=nt.td,le.option||(nt.optgroup=nt.option=[1,""]);var it=/<|&#?\w+;/,ot=/^([^.]*)(?:\.(.+)|)/;ge.event={global:{},add:function(e,t,n,i,o){var r,a,s,l,c,d,u,h,p,f,g,m=Be.get(e);if(Fe(e))for(n.handler&&(n=(r=n).handler,o=r.selector),o&&ge.find.matchesSelector(ze,o),n.guid||(n.guid=ge.guid++),(l=m.events)||(l=m.events=Object.create(null)),(a=m.handle)||(a=m.handle=function(t){return void 0!==ge&&ge.event.triggered!==t.type?ge.event.dispatch.apply(e,arguments):void 0}),c=(t=(t||"").match(Le)||[""]).length;c--;)p=g=(s=ot.exec(t[c])||[])[1],f=(s[2]||"").split(".").sort(),p&&(u=ge.event.special[p]||{},p=(o?u.delegateType:u.bindType)||p,u=ge.event.special[p]||{},d=ge.extend({type:p,origType:g,data:i,handler:n,guid:n.guid,selector:o,needsContext:o&&ge.expr.match.needsContext.test(o),namespace:f.join(".")},r),(h=l[p])||((h=l[p]=[]).delegateCount=0,u.setup&&!1!==u.setup.call(e,i,f,a)||e.addEventListener&&e.addEventListener(p,a)),u.add&&(u.add.call(e,d),d.handler.guid||(d.handler.guid=n.guid)),o?h.splice(h.delegateCount++,0,d):h.push(d),ge.event.global[p]=!0)},remove:function(e,t,n,i,o){var r,a,s,l,c,d,u,h,p,f,g,m=Be.hasData(e)&&Be.get(e);if(m&&(l=m.events)){for(c=(t=(t||"").match(Le)||[""]).length;c--;)if(p=g=(s=ot.exec(t[c])||[])[1],f=(s[2]||"").split(".").sort(),p){for(u=ge.event.special[p]||{},h=l[p=(i?u.delegateType:u.bindType)||p]||[],s=s[2]&&new RegExp("(^|\\.)"+f.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=r=h.length;r--;)d=h[r],!o&&g!==d.origType||n&&n.guid!==d.guid||s&&!s.test(d.namespace)||i&&i!==d.selector&&("**"!==i||!d.selector)||(h.splice(r,1),d.selector&&h.delegateCount--,u.remove&&u.remove.call(e,d));a&&!h.length&&(u.teardown&&!1!==u.teardown.call(e,f,m.handle)||ge.removeEvent(e,p,m.handle),delete l[p])}else for(p in l)ge.event.remove(e,p+t[c],n,i,!0);ge.isEmptyObject(l)&&Be.remove(e,"handle events")}},dispatch:function(e){var t,n,i,o,r,a,s=new Array(arguments.length),l=ge.event.fix(e),c=(Be.get(this,"events")||Object.create(null))[l.type]||[],d=ge.event.special[l.type]||{};for(s[0]=l,t=1;t\s*$/g;ge.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var i,o,r,a,s,l,c,d=e.cloneNode(!0),u=Ke(e);if(!(le.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||ge.isXMLDoc(e)))for(a=b(d),i=0,o=(r=b(e)).length;i").attr(e.scriptAttrs||{}).prop({charset:e.scriptCharset,src:e.url}).on("load error",n=function(e){t.remove(),n=null,e&&o("error"===e.type?404:200,e.type)}),ue.head.appendChild(t[0])},abort:function(){n&&n()}}});var Ut,qt=[],Qt=/(=)\?(?=&|$)|\?\?/;ge.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=qt.pop()||ge.expando+"_"+$t.guid++;return this[e]=!0,e}}),ge.ajaxPrefilter("json jsonp",function(t,n,i){var o,r,a,s=!1!==t.jsonp&&(Qt.test(t.url)?"url":"string"==typeof t.data&&0===(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&Qt.test(t.data)&&"data");if(s||"jsonp"===t.dataTypes[0])return o=t.jsonpCallback=ce(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,s?t[s]=t[s].replace(Qt,"$1"+o):!1!==t.jsonp&&(t.url+=(Lt.test(t.url)?"&":"?")+t.jsonp+"="+o),t.converters["script json"]=function(){return a||ge.error(o+" was not called"),a[0]},t.dataTypes[0]="json",r=e[o],e[o]=function(){a=arguments},i.always(function(){void 0===r?ge(e).removeProp(o):e[o]=r,t[o]&&(t.jsonpCallback=n.jsonpCallback,qt.push(o)),a&&ce(r)&&r(a[0]),a=r=void 0}),"script"}),le.createHTMLDocument=((Ut=ue.implementation.createHTMLDocument("").body).innerHTML="
    ",2===Ut.childNodes.length),ge.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(le.createHTMLDocument?((i=(t=ue.implementation.createHTMLDocument("")).createElement("base")).href=ue.location.href,t.head.appendChild(i)):t=ue),r=!n&&[],(o=Se.exec(e))?[t.createElement(o[1])]:(o=x([e],t,r),r&&r.length&&ge(r).remove(),ge.merge([],o.childNodes)));var i,o,r},ge.fn.load=function(e,t,n){var i,o,r,a=this,s=e.indexOf(" ");return-1").append(ge.parseHTML(e)).find(i):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,r||[e.responseText,t,e])})}),this},ge.expr.pseudos.animated=function(e){return ge.grep(ge.timers,function(t){return e===t.elem}).length},ge.offset={setOffset:function(e,t,n){var i,o,r,a,s,l,c=ge.css(e,"position"),d=ge(e),u={};"static"===c&&(e.style.position="relative"),s=d.offset(),r=ge.css(e,"top"),l=ge.css(e,"left"),("absolute"===c||"fixed"===c)&&-1<(r+l).indexOf("auto")?(a=(i=d.position()).top,o=i.left):(a=parseFloat(r)||0,o=parseFloat(l)||0),ce(t)&&(t=t.call(e,n,ge.extend({},s))),null!=t.top&&(u.top=t.top-s.top+a),null!=t.left&&(u.left=t.left-s.left+o),"using"in t?t.using.call(e,u):d.css(u)}},ge.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){ge.offset.setOffset(this,e,t)});var t,n,i=this[0];return i?i.getClientRects().length?(t=i.getBoundingClientRect(),n=i.ownerDocument.defaultView,{top:t.top+n.pageYOffset,left:t.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,i=this[0],o={top:0,left:0};if("fixed"===ge.css(i,"position"))t=i.getBoundingClientRect();else{for(t=this.offset(),n=i.ownerDocument,e=i.offsetParent||n.documentElement;e&&(e===n.body||e===n.documentElement)&&"static"===ge.css(e,"position");)e=e.parentNode;e&&e!==i&&1===e.nodeType&&((o=ge(e).offset()).top+=ge.css(e,"borderTopWidth",!0),o.left+=ge.css(e,"borderLeftWidth",!0))}return{top:t.top-o.top-ge.css(i,"marginTop",!0),left:t.left-o.left-ge.css(i,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent;e&&"static"===ge.css(e,"position");)e=e.offsetParent;return e||ze})}}),ge.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n="pageYOffset"===t;ge.fn[e]=function(i){return Ne(this,function(e,i,o){var r;if(de(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===o)return r?r[t]:e[i];r?r.scrollTo(n?r.pageXOffset:o,n?o:r.pageYOffset):e[i]=o},e,i,arguments.length)}}),ge.each(["top","left"],function(e,t){ge.cssHooks[t]=O(le.pixelPosition,function(e,n){if(n)return n=L(e,t),lt.test(n)?ge(e).position()[t]+"px":n})}),ge.each({Height:"height",Width:"width"},function(e,t){ge.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,i){ge.fn[i]=function(o,r){var a=arguments.length&&(n||"boolean"!=typeof o),s=n||(!0===o||!0===r?"margin":"border");return Ne(this,function(t,n,o){var r;return de(t)?0===i.indexOf("outer")?t["inner"+e]:t.document.documentElement["client"+e]:9===t.nodeType?(r=t.documentElement,Math.max(t.body["scroll"+e],r["scroll"+e],t.body["offset"+e],r["offset"+e],r["client"+e])):void 0===o?ge.css(t,n,s):ge.style(t,n,o,s)},t,a?o:void 0,a)}})}),ge.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){ge.fn[t]=function(e){return this.on(t,e)}}),ge.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,i){return this.on(t,e,n,i)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.on("mouseenter",e).on("mouseleave",t||e)}}),ge.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,t){ge.fn[t]=function(e,n){return 0=2&&(!n[3]||-1!==n[3].indexOf("css=true"))&&layui.link(n[1]+"cascader"+(n[2]||"")+".css")}(window),layui.define(["jquery"],function(e){function t(e){e=e||window.event,e.stopPropagation?e.stopPropagation():e.cancelBubble=!0}function n(e,t,n,i){this.data=e,this.cascader=t,this.config=t.config,this.props=t.props,this.level=n,this.parentNode=i,this.icons=t.icons,this._checked=0,this._loading=!1,this.nodeId=t.data.nodeId++}function i(e){this.config=o.extend(!0,{elem:"",value:null,options:[],empty:"暂无数据",placeholder:"请选择",disabled:!1,clearable:!1,showAllLevels:!0,collapseTags:!1,minCollapseTagsNumber:1,separator:" / ",filterable:!1,filterMethod:function(e,t){return e.path.some(function(e){return-1!==e.label.indexOf(t)})},debounce:300,beforeFilter:function(e){return!0},popperClass:"",extendClass:!1,extendStyle:!1,disabledFixed:!1,maxSize:0,props:{strictMode:!1,expandTrigger:"click",multiple:!1,checkStrictly:!1,lazy:!1,lazyLoad:function(e,t){},value:"value",label:"label",children:"children",disabled:"disabled",leaf:"leaf"}},e),this.data={nodeId:1,nodes:[],menuData:[],activeNodeId:null,activeNode:null,checkedNodeIds:[],checkedNodes:[]},this.showPanel=!1,this.event={change:[],open:[],close:[]},this.filtering=!1,this._init(),this.closeEventId=0, +this._maxSizeMode=null}var o=layui.jquery;n.prototype={constructor:n,get topParentNode(){return!this.parentNode&&this||this.topParentNode},childrenNode:void 0,get loading(){return this._loading},set loading(e){var t=this.$li;if(t){var n=this.icons.right,i=this.icons.loading,o=t.find("i");e?(o.addClass(i),o.removeClass(n)):(o.addClass(n),o.removeClass(i))}return this._loading=e},get label(){return this.data[this.props.label]},get value(){return this.data[this.props.value]},get disabled(){var e=this.props.multiple,t=this.config.maxSize,n=this.cascader.data.checkedNodeIds,i=this.props.disabled,o=this.props.checkStrictly;if(e&&0!==t&&n.length>=t&&-1===n.indexOf(this.nodeId)){if(o)return!0;if(!this.getAllLeafChildren().map(function(e){return e.nodeId}).some(function(e){return-1!==n.indexOf(e)}))return!0}return o?this.data[i]:this.path.some(function(e){return e.data[i]})},get children(){return this.data[this.props.children]},set children(e){this.data[this.props.children]=e},get leaf(){var e=this.data[this.props.leaf];return"boolean"==typeof e?e:!this.children||this.children.length<=0},get activeNodeId(){return this.cascader.data.activeNodeId},get checkedNodeIds(){return this.cascader.data.checkedNodeIds},get path(){var e=this.parentNode;return e?e.path.concat([this]):[this]},get isFiltering(){return this.cascader.isFiltering},get $tag(){var e=this.cascader,n=this.config.showAllLevels,i=this.config.disabled,o=this.disabled,r=this.config.disabledFixed,a=this.getPathLabel(n),s=e.get$tag(a,!(i||o&&r)),l=this;return s.find("i").click(function(n){t(n),l.selectedValue(),e.removeTag(l.value,l)}),s},getPathLabel:function(e){var t=this.path,n=this.config.separator;return e?t.map(function(e){return e.label}).join(n):t[t.length-1].label},init:function(){var e=this.props.multiple,t=this.props.checkStrictly,n=this.icons.from,i=this.icons.right,r="",a=this.label;this.leaf||(r=i),this.$li=o(''),e||t?!e&&t?this._renderRadioCheckStrictly():e&&!t?this._renderMultiple():e&&t&&this._renderMultipleCheckStrictly():this._renderRadio()},initSuggestionLi:function(){var e=this.getPathLabel(!0);this.$suggestionLi=o('
  • '+e+"
  • "),this._renderFiltering()},bind:function(e){this.init(),e.append(this.$li)},bindSuggestion:function(e){this.initSuggestionLi(),e.append(this.$suggestionLi)},_renderFiltering:function(){var e=this.$suggestionLi,n=this.nodeId,i=this.icons.from,o=this.icons.ok,r=this,a=this.cascader,s=this.props.multiple,l='';e.click(function(i){t(i),r.selectedValue(),s?-1===r.checkedNodeIds.indexOf(n)?(e.removeClass("is-checked"),e.find(".el-icon-check").remove()):(e.addClass("is-checked"),e.append(l)):a.close()}),(s&&-1!==r.checkedNodeIds.indexOf(n)||!s&&r.activeNodeId===n)&&(e.addClass("is-checked"),e.append(l))},_renderRadio:function(){var e=this.$li,n=this.nodeId,i=this.icons.from,o=this.icons.ok,r=this.level,a=this.leaf,s=this,l=this.cascader,c=this.cascader.data.activeNode,d=this.parentNode;if(s.activeNodeId&&c.path.some(function(e){return e.nodeId===n})&&(s.activeNodeId===n&&e.prepend(''),e.addClass("is-active"),e.addClass("in-checked-path")),this.disabled)return void e.addClass("is-disabled");e.addClass("is-selectable"),d&&(d.$li.siblings().removeClass("in-active-path"),d.$li.addClass("in-active-path")),this._liClick(function(e){t(e);var n=s.childrenNode;a&&"click"===e.type&&(s.selectedValue(),l.close()),l._appendMenu(n,r+1,s)})},_renderRadioCheckStrictly:function(){var e=this.$li,n=this.nodeId,i=this.level,r=this.leaf,a=this,s=this.cascader,l=s.data.activeNode,c=this.parentNode;e.addClass("is-selectable");var d=o('');if(this.$radio=d,e.prepend(d),c&&(c.$li.siblings().removeClass("in-active-path"),c.$li.addClass("in-active-path")),this._liClick(function(e){t(e);var n=a.childrenNode;!a.disabled&&r&&"click"===e.type&&a.selectedValue(),s._appendMenu(n,i+1,a)}),a.activeNodeId&&l.path.some(function(e){return e.nodeId===n})&&(a.activeNodeId===n&&d.find(".el-radio__input").addClass("is-checked"),e.addClass("is-active"),e.addClass("in-checked-path")),this.disabled)return d.addClass("is-disabled"),void d.find(".el-radio__input").addClass("is-disabled");d.click(function(e){e.preventDefault(),!r&&a.selectedValue()})},_renderMultiple:function(){var e=this.$li,n=this.level,i=this.leaf,r=this,a=this.cascader,s=this._checked,l=this.parentNode;e.addClass("el-cascader-node");var c=o('');if(this.$checked=c,e.prepend(c),1===s?this.$checked.find(".el-checkbox__input").addClass("is-checked"):2===s&&this.$checked.find(".el-checkbox__input").addClass("is-indeterminate"),l&&(l.$li.siblings().removeClass("in-active-path"),l.$li.addClass("in-active-path")),this._liClick(function(e){t(e);var o=r.childrenNode;!r.disabled&&i&&"click"===e.type&&r.selectedValue(),a._appendMenu(o,n+1,r)}),this.disabled)return e.addClass("is-disabled"),c.addClass("is-disabled"),void c.find(".el-checkbox__input").addClass("is-disabled");c.click(function(e){if(e.preventDefault(),!i){var t=r.childrenNode;r.selectedValue(),a._appendMenu(t,n+1,r)}})},_renderMultipleCheckStrictly:function(){var e=this.$li,n=this.level,i=this.leaf,r=this,a=this.cascader,s=a.data.checkedNodeIds,l=a.data.checkedNodes,c=this.nodeId,d=this.parentNode;e.addClass("el-cascader-node is-selectable");var u=o('');if(this.$checked=u,e.prepend(u),l.some(function(e){return e.path.some(function(e){return e.nodeId===c})})&&(e.addClass("in-checked-path"),-1!==s.indexOf(c)&&this.$checked.find(".el-checkbox__input").addClass("is-checked")),d&&(d.$li.siblings().removeClass("in-active-path"),d.$li.addClass("in-active-path")),this._liClick(function(e){t(e);var o=r.childrenNode;!r.disabled&&i&&"click"===e.type&&r.selectedValue(),a._appendMenu(o,n+1,r)}),this.disabled)return u.addClass("is-disabled"),void u.find(".el-checkbox__input").addClass("is-disabled");u.click(function(e){if(e.preventDefault(),!i){r.selectedValue();var t=r.childrenNode;a._appendMenu(t,n+1,r)}})},transferParent:function(e,t,n){if(n||(n=this),this!==n||t){if(!1===(e&&e(this)))return}this.parentNode&&this.parentNode.transferParent(e,t,n)},transferChildren:function(e,t,n){if(n||(n=this),this!==n||t){if(!1===(e&&e(this)))return}var i=this.getChildren();if(i&&i.length>0)for(var o in i)i[o].transferChildren(e,t,n)},selectedValue:function(){var e=this.nodeId,t=this.cascader,n=this.props.multiple,i=this.props.checkStrictly,o=this.leaf;if(n||!o&&!i){if(n){var r,a=t.data.checkedNodeIds,s=t.data.checkedNodes,l=this.config.disabledFixed;if(i){var c=a.indexOf(e);-1===c?r=s.concat([this]):(r=s.concat(),r.splice(c,1))}else{var d=this.getAllLeafChildren();if(1===(1!==this._checked&&l?this._getMultipleChecked(d):this._checked))r=s.filter(function(e){return!d.some(function(t){return e.nodeId===t.nodeId})});else{var u=d.filter(function(e){return-1===a.indexOf(e.nodeId)});r=s.concat(u)}}var h=r.map(function(e){return e.nodeId});t._setCheckedValue(h,r)}}else t._setActiveValue(e,this)},_liLoad:function(e,t){var n=this.leaf,i=this.props.lazy,o=this.props.lazyLoad,r=this.children,a=this,s=this.cascader,l=this.level,c=this.props.multiple,d=this.props.checkStrictly;n||r&&0!==r.length||!i?t&&t(e):a.loading||(a.loading=!0,o(a,function(n){a.loading=!1,a.setChildren(s.initNodes(n,l+1,a)),a.children=n,t&&t(e),c&&!d&&a.transferParent(function(e){e.syncStyle()},!0)}))},_liClick:function(e){function t(t){o._liLoad(t,e)}var n=this.leaf,i=this.$li,o=this;("click"===this.props.expandTrigger||n)&&i.click(t),"hover"===this.props.expandTrigger&&i.mouseenter(t)},setChildren:function(e){this.childrenNode=e},getChildren:function(){return this.childrenNode},syncStyle:function(){var e=this.props.multiple,t=this.props.checkStrictly;e?t?this._sync.syncMultipleCheckStrictly(this):this._sync.syncMultiple(this):t?this._sync.syncRadioCheckStrictly(this):this._sync.syncRadio(this)},_sync:{syncRadio:function(e){var t=e.$li,n=e.icons.from,i=e.icons.ok,o=e.props.multiple,r=e.props.checkStrictly,a=e.nodeId;if(t&&!o&&!r){var s=e.cascader.data.activeNode;if(e.activeNodeId===a){0===t.find("."+i).length&&t.prepend('')}else t.find("."+i).remove();s&&s.path.some(function(e){return e.nodeId===a})?(t.addClass("is-active"),t.addClass("in-checked-path")):(t.removeClass("is-active"),t.removeClass("in-checked-path"))}},syncRadioCheckStrictly:function(e){var t=e.$li,n=e.props.checkStrictly,i=e.props.multiple;if(t&&!i&&n){var o=e.$radio,r=e.cascader.data.activeNode,a=e.nodeId;e.activeNodeId===a?o.find(".el-radio__input").addClass("is-checked"):o.find(".el-radio__input").removeClass("is-checked"),r&&r.path.some(function(e){return e.nodeId===a})?(t.addClass("is-active"),t.addClass("in-checked-path")):(t.removeClass("is-active"),t.removeClass("in-checked-path"))}},syncMultiple:function(e){var t=e.$li,n=e.props.checkStrictly,i=e.props.multiple,o=e.config.disabledFixed;if(i&&!n){var r=e.getAllLeafChildren(o),a=e._getMultipleChecked(r);if(e._checked=a,t){var s=e.$checked.find(".el-checkbox__input");0===a?(s.removeClass("is-checked"),s.removeClass("is-indeterminate")):1===a?(s.removeClass("is-indeterminate"),s.addClass("is-checked")):2===a&&(s.removeClass("is-checked"),s.addClass("is-indeterminate"))}}},syncMultipleCheckStrictly:function(e){var t=e.$li,n=e.props.checkStrictly,i=e.props.multiple;if(t&&i&&n){var o=e.cascader.data.checkedNodes,r=e.checkedNodeIds,a=e.nodeId,s=o.some(function(e){return e.path.some(function(e){return e.nodeId===a})}),l=e.$checked.find(".el-checkbox__input");r.some(function(e){return e===a})?l.addClass("is-checked"):l.removeClass("is-checked"),s?t.addClass("in-checked-path"):t.removeClass("in-checked-path")}}},getAllLeafChildren:function(e){if(this.leaf)return[this];var t=[];return this.transferChildren(function(n){if(n.disabled&&!e)return!1;n.leaf&&t.push(n)}),t},expandPanel:function(){var e=this.path,t=this.cascader;e.forEach(function(e,n,i){if(n!==i.length-1){var o=e.childrenNode;t._appendMenu(o,e.level+1,e.parentNode)}})},_getMultipleChecked:function(e){for(var t=this.cascader,n=t.data.checkedNodeIds,i=e.map(function(e){return e.nodeId}),o=0,r=0;r0&&1!==o?2:1:1===o?2:0}return o}},i.prototype={constructor:i,get props(){return this.config.props},get isFiltering(){return this.filtering},set isFiltering(e){if(this.filtering!==e){this.filtering=!!e;var t=this.$panel;this.filtering?(t.find(".el-cascader-panel").hide(),t.find(".el-cascader__suggestion-panel").show()):(t.find(".el-cascader-panel").show(),t.find(".el-cascader__suggestion-panel").hide(),this.$tagsInput&&this.$tagsInput.val(""))}},set maxSizeMode(e){this._maxSizeMode!==e&&(this._maxSizeMode=e,this.refreshMenu())},icons:{from:"layui-icon",down:"layui-icon-down",close:"layui-icon-close",right:"layui-icon-right",ok:"layui-icon-ok",loading:"layui-icon-loading-1 layui-anim layui-anim-rotate layui-anim-loop"},_init:function(){this._checkConfig(),this._initInput(),this._initPanel(),this.setOptions(this.config.options);var e=this;o(window).scroll(function(){e._resetXY()}),o(window).resize(function(){e._resetXY()}),this.$div.click(function(t){if(!e.config.disabled){e.showPanel?e.close():e.open()}})},_checkConfig:function(){var e=this.config.elem;if(!e||0===o(e).length)throw new Error("缺少elem节点选择器");var t=this.config.maxSize;if("number"!=typeof t||t<0)throw new Error("maxSize应是一个大于等于0的有效的number值");if(!Array.isArray(this.config.options))throw new Error("options不是一个有效的数组")},_initRoot:function(){var e=this.props.lazy,t=this.props.lazyLoad,n=this,i=this.data.nodes;i.length>0||!e?this._appendMenu(i,0):e&&(this._appendMenu(i,0),t({root:!0,level:0},function(e){n.data.nodes=n.initNodes(e,0,null),n._appendMenu(n.data.nodes,0)}))},setOptions:function(e){this.config.options=e,this.data.nodes=this.initNodes(e,0,null),this._initRoot(),this.setValue(this.config.value)},_resetXY:function(){var e=this.$div,t=e.offset(),n=this.$panel;if(n){var i=window.innerHeight,o=window.innerWidth,r=n.height(),a=n.width(),s=e.height(),l=e[0].getBoundingClientRect(),c=n.find(".popper__arrow"),d=Math.min(o-l.x-a-5,0);if(i-(l.top+s)r+20)n.attr("x-placement","top-start"),n.css({top:t.top-20-r+"px",left:t.left+d+"px"});else{n.attr("x-placement","bottom-start");var u=Math.max(r-(i-l.y-s-15),0);n.css({top:t.top+s-u+"px",left:t.left+d+"px"})}c.css("left",35-d+"px")}},get $menus(){return this.$panel&&this.$panel.find(".el-cascader-panel .el-cascader-menu")},_initInput:function(){var e=o(this.config.elem),t=this;null===this.config.value&&e.attr("value")&&(this.config.value=e.attr("value"));var n=this.config.placeholder,i=this.icons.from,r=this.icons.down,a=this.props.multiple,s=this.config.extendClass,l=this.config.extendStyle;if(this.$div=o('
    '),l){var c=e.attr("style");c&&this.$div.attr("style",c)}if(s){var d=e.attr("class");d&&d.split(" ").forEach(function(e){t.$div.addClass(e)})}this.$input=o('
    '),this.$div.append(this.$input),this.$inputRow=this.$input.find(".el-input__inner"),a&&(this.$tags=o('
    \x3c!----\x3e
    '),this.$div.append(this.$tags)),this._initHideElement(e).after(this.$div),this.$icon=this.$input.find("i"),this._initFilterableInputEvent(),this.disabled(this.config.disabled)},_initHideElement:function(e){if("input"===e.prop("tagName").toLowerCase())return e.hide(),e.attr("type","hidden"),this.$ec=e,e;var t=e[0].attributes,n=o(""),i=Object.keys(t);for(var r in i){var a=t[r];n.attr(a.name,a.value)}return n.hide(),n.attr("type","hidden"),this.$ec=n,e.before(n),e.remove(),n},_initFilterableInputEvent:function(){function e(e){var t=this;n&&clearTimeout(n),n=setTimeout(function(){n=null;var e=o(t).val();if(!e)return void(d.isFiltering=!1);if(d.open(),"function"==typeof s&&s(e)){d.isFiltering=!0;var i=d.getNodes(),r=i.filter(function(t){var n;return n=c?t.disabled:t.path.some(function(e){return e.disabled}),!(!t.leaf&&!c||n||"function"!=typeof l||!l(t,e))});d._setSuggestionMenu(r)}},r)}if(this.config.filterable){var n,i=this.props.multiple,r=this.config.debounce,a=this.config.placeholder,s=this.config.beforeFilter,l=this.config.filterMethod,c=this.props.checkStrictly,d=this;if(i){this.$tagsInput=o('');var u=this.$tagsInput;this.$tags.append(u),u.on("keydown",e),u.click(function(e){d.isFiltering&&t(e)})}else{var h=this.$inputRow;h.removeAttr("readonly"),h.on("keydown",e),h.click(function(e){d.isFiltering&&t(e)})}}},_initPanel:function(){var e=this.$panel,n=this.config.popperClass||"";e||(this.$panel=o(''),e=this.$panel,e.appendTo("body"),e.click(function(e){t(e)}),this._initSuggestionPanel())},_appendMenu:function(e,t,n,i){if(this._removeMenu(t),!n||!n.leaf){var r=this.data.menuData,a=o('');this.$panel.find(".el-cascader-panel").append(a),this._appendLi(a,e);var s={nodes:e,level:t,parentNode:n,scrollbar:{top:0,left:0}};i&&(s.scrollbar=i.scrollbar),this._initScrollbar(a,s),this._resetXY(),r.push(s)}},_removeMenu:function(e){var t=e-1;-1!==t?this.$panel.find(".el-cascader-panel .el-cascader-menu:gt("+t+")").remove():this.$panel.find(".el-cascader-panel .el-cascader-menu").remove();var n=this.data.menuData;n.length>e&&n.splice(e,n.length-e)},_appendLi:function(e,t){var n=e.find(".el-cascader-menu__list");if(!t||0===t.length){var i=this.config.empty;return void n.append('
    '+i+"
    ")}o.each(t,function(e,t){t.bind(n)})},refreshMenu:function(){var e=this;this.data.menuData.concat([]).forEach(function(t){e._appendMenu(t.nodes,t.level,t.parentNode,t)})},_initSuggestionPanel:function(){if(this.config.filterable){var e=this.$suggestionPanel;e||(this.$suggestionPanel=o(''),e=this.$suggestionPanel,this.$panel.find(".popper__arrow").before(e),e.click(function(e){t(e)}))}},_setSuggestionMenu:function(e){var t=this.$suggestionPanel,n=t.find(".el-cascader__suggestion-list");if(n.empty(),t.find(".el-scrollbar__bar").remove(),!e||0===e.length)return void n.append('
  • 无匹配数据
  • ');o.each(e,function(e,t){t.bindSuggestion(n)}),this._initScrollbar(t,{scrollbar:{top:0,left:0}}),this._resetXY()},initNodes:function(e,t,i){var o=[];for(var r in e){var a=e[r],s=new n(a,this,t,i);null!==s.value&&void 0!==s.value&&(o.push(s),s.children&&s.children.length>0&&s.setChildren(this.initNodes(s.children,t+1,s)))}return o},_setActiveValue:function(e,t){if(this.data.activeNodeId!==e){var n=this.data.activeNode;this.data.activeNodeId=e,this.data.activeNode=t,n&&n.transferParent(function(e){e.syncStyle()},!0),t&&t.transferParent(function(e){e.syncStyle()},!0),this.change(t&&t.value,t),null!==e&&this._setClear()}},_setCheckedValue:function(e,t){var n,i=this.data.checkedNodes,o=this.config.maxSize;e.length>0&&0!==o&&e.length>=o?(e=e.slice(0,o),t=t.slice(0,o),n=!0):n=!1,this.data.checkedNodeIds=e||[],this.data.checkedNodes=t||[];var r=[],a=[];i.forEach(function(e){e.path.forEach(function(e){-1===a.indexOf(e.nodeId)&&(r.push(e),a.push(e.nodeId))})}),t.forEach(function(e){e.path.forEach(function(e){-1===a.indexOf(e.nodeId)&&(r.push(e),a.push(e.nodeId))})}),r.forEach(function(e){e.syncStyle()}),this.change(t.map(function(e){return e.value}),t),this._setClear(),this.maxSizeMode=n},setValue:function(e){if((this.data.activeNodeId||this.data.checkedNodeIds.length>0)&&this.clearCheckedNodes(),e){var t=this.props.strictMode;if(t&&!Array.isArray(e))throw new Error("严格模式下,value必须是一个包含父子节点数组结构.");var n=this.getNodes(this.data.nodes),i=this.props.checkStrictly,o=this.props.multiple,r=this.config.disabledFixed;if(o){var a=n.filter(function(n){return!(!i&&!n.leaf||n.disabled&&!r)&&(t?e.some(function(e){if(!Array.isArray(e))throw new Error("多选严格模式下,value必须是一个二维数组结构.");var t=n.path;return e.length===t.length&&e.every(function(e,n){return t[n].value===e})}):-1!==e.indexOf(n.value))}),s=a.map(function(e){return e.nodeId});if(this._setCheckedValue(s,a),a.length>0){a[0].expandPanel()}}else for(var l=0;l
    ');e.append(r);var a=o(r[1]).find(".el-scrollbar__thumb"),s=o(r[0]).find(".el-scrollbar__thumb"),l=e.find(".el-scrollbar__wrap"),c=this.$panel,d=e.find("li"),u=Math.max(c.height(),e.height()),h=(u-6)/(d.height()*d.length),p=c.width()/d.width();a.mousedown(function(e){e.stopImmediatePropagation(),t(e);var n=function(){return!1};o(document).bind("selectstart",n);var i=e.clientY,r=l.scrollTop(),a=function(e){e.stopImmediatePropagation();var t=r+(e.clientY-i)/h;l.scrollTop(t)};o(document).bind("mousemove",a),o(document).one("mouseup",function(e){t(e),e.stopImmediatePropagation(),o(document).off("mousemove",a),o(document).off("selectstart",n)})}),l.scroll(function(){var e=o(this);n.scrollbar.top=e.scrollTop(),n.scrollbar.left=e.scrollLeft(),i(n.scrollbar.top,n.scrollbar.left)}),l.scrollTop(n.scrollbar.top),i(n.scrollbar.top,n.scrollbar.left)},_fillingPath:function(){var e=this.props.multiple,t=this.config.showAllLevels,n=this.config.separator,i=this.config.collapseTags,o=this.$inputRow,r=this.config.placeholder,a=this;if(e){this.$tags.find(".el-tag").remove();var s=this.$tagsInput;o.css("height","");var l=this.data.checkedNodes,c=Math.max(this.config.minCollapseTagsNumber,1);if(l.length>0){var d=[],u=l;i&&(u=l.slice(0,Math.min(l.length,c))),u.forEach(function(e){d.push(e.$tag)}),i&&l.length>c&&d.push(a.get$tag("+ "+(l.length-c),!1)),d.forEach(function(e){s?s.before(e):a.$tags.append(e)})}var h=a.$tags.height();h>o.height()&&o.css("height",h+4+"px"),this._resetXY(),l.length>0?(o.removeAttr("placeholder"),s&&s.removeAttr("placeholder",r)):(o.attr("placeholder",r),s&&s.attr("placeholder",r))}else{var p=this.data.activeNode,f=p&&p.path||[];t?this._$inputRowSetValue(f.map(function(e){return e.label}).join(n)):this._$inputRowSetValue(p&&p.label||"")}},_$inputRowSetValue:function(e){e=e||"";var t=this.$inputRow;t.attr("value",e),t.val(e)},get$tag:function(e,t){var n=this.icons.from,i=this.icons.close;return o(''+e+""+(t?'':"")+"")},_setClear:function(){function e(){i.$icon.removeClass(i.icons.down),i.$icon.addClass(i.icons.close)}function n(){i.$icon.removeClass(i.icons.close),i.$icon.addClass(i.icons.down)}var i=this;i.$div.mouseenter(function(){e()}),i.$div.mouseleave(function(){n()}),i.$icon.off("click");var o,r=this.props.multiple;o=r?this.data.checkedNodeIds.length>0:!!this.data.activeNodeId,o&&!this.config.disabled&&this.config.clearable?i.$icon.one("click",function(e){t(e),i.close(),i.clearCheckedNodes(),n(),i.$icon.off("mouseenter"),i.$div.off("mouseenter"),i.$div.off("mouseleave")}):(n(),i.$icon.off("mouseenter"),i.$div.off("mouseenter"),i.$div.off("mouseleave"))},disabled:function(e){this.config.disabled=!!e,this.config.disabled?(this.$div.addClass("is-disabled"),this.$div.find(".el-input--suffix").addClass("is-disabled"),this.$inputRow.attr("disabled","disabled"),this.$tagsInput&&this.$tagsInput.attr("disabled","disabled").hide()):(this.$div.removeClass("is-disabled"),this.$div.find(".el-input--suffix").removeClass("is-disabled"),this.$inputRow.removeAttr("disabled"),this.$tagsInput&&this.$tagsInput.removeAttr("disabled").show()),this._setClear(),this._fillingPath()},change:function(e,t){this.props.multiple?e&&e.length>0?this.$ec.attr("value",JSON.stringify(e)):this.$ec.removeAttr("value"):this.$ec.attr("value",e||""),this._fillingPath(),this.event.change.forEach(function(n){"function"==typeof n&&n(e,t)})},close:function(e){if(this.showPanel&&(!e||this.closeEventId===e)){this.showPanel=!1,this.$div.find(".layui-icon-down").removeClass("is-reverse"),this.$panel.slideUp(100),this.visibleChange(!1),this.$input.removeClass("is-focus");this.config.filterable&&(this.isFiltering=!1,this._fillingPath()),this.event.close.forEach(function(e){"function"==typeof e&&e()})}},open:function(){if(!this.showPanel){this.showPanel=!0,this.closeEventId++;var e=this;setTimeout(function(){o(document).one("click",e.close.bind(e,e.closeEventId))}),this._resetXY(),this.$div.find(".layui-icon-down").addClass("is-reverse"),this.$panel.slideDown(200),this.visibleChange(!0),this.$input.addClass("is-focus"),this.event.open.forEach(function(e){"function"==typeof e&&e()})}},visibleChange:function(e){},removeTag:function(e,t){},getCheckedValues:function(){var e=this.props.strictMode;if(this.props.multiple){var t=this.data.checkedNodes;return e?t.map(function(e){return e.path.map(function(e){return e.value})}):t.map(function(e){return e.value})}var n=this.data.activeNode;return e?n&&n.path.map(function(e){return e.value}):n&&n.value},getCheckedNodes:function(){var e=this.props.strictMode;if(this.props.multiple){var t=this.data.checkedNodes;return e?t&&t.map(function(e){return e.path}):t}var n=this.data.activeNode;return e?n&&n.path:n},clearCheckedNodes:function(e){if(this.props.multiple){var t=this.config.disabledFixed;if(!e&&t){var n=this.data.checkedNodes,i=n.filter(function(e){return e.disabled}),o=i.map(function(e){return e.nodeId});this._setCheckedValue(o,i)}else this._setCheckedValue([],[])}else this._setActiveValue(null,null)}};var r=function(){var e=this;return{setOptions:function(t){e.setOptions(t)},setValue:function(t){e.setValue(t)},changeEvent:function(t){e.event.change.push(t)},closeEvent:function(t){e.event.close.push(t)},openEvent:function(t){e.event.open.push(t)},disabled:function(t){e.disabled(t)},close:function(){e.close()},open:function(){e.open()},getCheckedNodes:function(){return e.getCheckedNodes()},getCheckedValues:function(){return e.getCheckedValues()},clearCheckedNodes:function(t){e.clearCheckedNodes(t)},expandNode:function(t){for(var n=e.getNodes(e.data.nodes),i=0;ic.top&&(o=!1),i?0:l.top-s.top);o?(i=t.offsetHeight,this.dom.menu.style.left="0",this.dom.menu.style.top=n+i+"px",this.dom.menu.style.bottom=""):(this.dom.menu.style.left="0",this.dom.menu.style.top="",this.dom.menu.style.bottom="0px"),this.limitHeight&&(s=o?c.bottom-l.bottom-10:l.top-c.top-10,this.dom.list.style.maxHeight=s+"px",this.dom.list.style.overflowY="auto"),this.dom.absoluteAnchor.appendChild(this.dom.root),this.selection=(0,a.getSelection)(), +this.anchor=t,setTimeout(function(){d.dom.focusButton.focus()},0),e.visibleMenu&&e.visibleMenu.hide(),e.visibleMenu=this}},{key:"hide",value:function(){this.dom.absoluteAnchor&&(this.dom.absoluteAnchor.destroy(),delete this.dom.absoluteAnchor),this.dom.root.parentNode&&(this.dom.root.parentNode.removeChild(this.dom.root),this.onClose)&&this.onClose(),e.visibleMenu===this&&(e.visibleMenu=void 0)}},{key:"_onExpandItem",value:function(e){var t,n=this,i=e===this.expandedItem,o=this.expandedItem;o&&(o.ul.style.height="0",o.ul.style.padding="",setTimeout(function(){n.expandedItem!==o&&(o.ul.style.display="",a.removeClassName)(o.ul.parentNode,"jsoneditor-selected")},300),this.expandedItem=void 0),i||((t=e.ul).style.display="block",t.clientHeight,setTimeout(function(){if(n.expandedItem===e){for(var i=0,o=0;o/gi,"\n"))),a.appendChild(s),r.appendChild(a)),r.onclick=function(){o.onFocusLine(i)},n.appendChild(r)}),this.dom.validationErrors=r,this.dom.validationErrorsContainer.appendChild(r),this.dom.additionalErrorsIndication.title=e.length+" errors total",this.dom.validationErrorsContainer.clientHeighte[0].length)||(e=t,n=r,this.options.flex));r++);return e?((i=e[0].match(/\n.*/g))&&(this.yylineno+=i.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:i?i[i.length-1].length-1:this.yylloc.last_column+e[0].length},this.yytext+=e[0],this.match+=e[0],this.yyleng=this.yytext.length,this._more=!1,this._input=this._input.slice(e[0].length),this.matched+=e[0],i=this.performAction.call(this,this.yy,this,o[n],this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),i||void 0):""===this._input?this.EOF:void this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var e=this.next();return void 0!==e?e:this.lex()},begin:function(e){this.conditionStack.push(e)},popState:function(){return this.conditionStack.pop()},_currentRules:function(){return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules},topState:function(){return this.conditionStack[this.conditionStack.length-2]},pushState:function(e){this.begin(e)},options:{},performAction:function(e,t,n,i){switch(n){case 0:break;case 1:return 6;case 2:return t.yytext=t.yytext.substr(1,t.yyleng-2),4;case 3:return 17;case 4:return 18;case 5:return 23;case 6:return 24;case 7:return 22;case 8:return 21;case 9:return 10;case 10:return 11;case 11:return 8;case 12:return 14;case 13:return"INVALID"}},rules:[/^(?:\s+)/,/^(?:(-?([0-9]|[1-9][0-9]+))(\.[0-9]+)?([eE][-+]?[0-9]+)?\b)/,/^(?:"(?:\\[\\"bfnrt/]|\\u[a-fA-F0-9]{4}|[^\\\0-\x09\x0a-\x1f"])*")/,/^(?:\{)/,/^(?:\})/,/^(?:\[)/,/^(?:\])/,/^(?:,)/,/^(?::)/,/^(?:true\b)/,/^(?:false\b)/,/^(?:null\b)/,/^(?:$)/,/^(?:.)/],conditions:{INITIAL:{rules:[0,1,2,3,4,5,6,7,8,9,10,11,12,13],inclusive:!0}}};var n;t.parser=n,t.parse=n.parse.bind(n)},4772:function(e){"use strict";function t(){}function n(e,t){return e.hasOwnProperty(t)&&(!0===e[t]||e[t].length)}function i(e,t,n){e.parentNode?e.parentNode.parentNode||t.appendChild(e.parentNode):t.appendChild(e),c.removeClass(e,"excluded"),n||(e.textContent=e.textContent)}function o(){var e,t;this.items.length&&(e=document.createDocumentFragment(),this.config.pagination?(t=this.pages.slice(0,this.pageIndex),c.each(t,function(t,n){c.each(n,function(t,n){i(n,e,this.customOption)},this)},this)):c.each(this.items,function(t,n){i(n,e,this.customOption)},this),e.childElementCount&&(c.removeClass(this.items[this.navIndex],"active"),this.navIndex=(e.querySelector(".selectr-option.selected")||e.querySelector(".selectr-option")).idx,c.addClass(this.items[this.navIndex],"active")),this.tree.appendChild(e))}function r(e,t){t=t||e;var n={class:"selectr-option",role:"treeitem","aria-selected":!1};return this.customOption?n.html=this.config.renderOption(t):n.textContent=e.textContent,(t=c.createElement("li",n)).idx=e.idx,this.items.push(t),e.defaultSelected&&this.defaultSelected.push(e.idx),e.disabled&&(t.disabled=!0,c.addClass(t,"disabled")),t}function a(){var e,t=this.tree,n=t.scrollTop;t.scrollHeight-t.offsetHeight<=n&&this.pageIndex"+t.label+""}),c.each(t.children,function(e,t){t.idx=u,d.appendChild(r.call(this,t,d)),u++},this)):(t.idx=u,r.call(this,t),u++)},this),this.config.data&&Array.isArray(this.config.data)&&(a=!(this.data=[]),d=!1,u=0,c.each(this.config.data,function(e,t){n(t,"children")?(a=c.createElement("optgroup",{label:t.text}),d=c.createElement("ul",{class:"selectr-optgroup",role:"group",html:"
  • "+t.text+"
  • "}),c.each(t.children,function(e,t){(s=new Option(t.text,t.value,!1,t.hasOwnProperty("selected")&&!0===t.selected)).disabled=n(t,"disabled"),this.options.push(s),a.appendChild(s),s.idx=u,d.appendChild(r.call(this,s,t)),this.data[u]=t,u++},this),this.el.appendChild(a)):((s=new Option(t.text,t.value,!1,t.hasOwnProperty("selected")&&!0===t.selected)).disabled=n(t,"disabled"),this.options.push(s),s.idx=u,r.call(this,s,t),this.data[u]=t,u++)},this)),this.setSelected(!0);for(var h=this.navIndex=0;hthis.tree.lastElementChild.idx){this.navIndex=this.tree.lastElementChild.idx;break}if(this.navIndexthis.optsRect.top+this.optsRect.height&&(this.tree.scrollTop=this.tree.scrollTop+(e.top+e.height-(this.optsRect.top+this.optsRect.height))),this.navIndex===this.tree.childElementCount-1&&this.requiresPagination&&a.call(this)):0===this.navIndex?this.tree.scrollTop=0:e.top-this.optsRect.top<0&&(this.tree.scrollTop=this.tree.scrollTop+(e.top-this.optsRect.top)),n&&c.removeClass(n,"active"),c.addClass(this.items[this.navIndex],"active")}else this.navigating=!1}.bind(this),this.events.reset=this.reset.bind(this),(this.config.nativeDropdown||this.mobileDevice)&&(this.container.addEventListener("touchstart",function(e){e.changedTouches[0].target===t.el&&t.toggle()},!!this.supportsEventPassiveOption&&{passive:!0}),this.container.addEventListener("click",function(e){e.target===t.el&&t.toggle()}),this.el.addEventListener("change",function(e){e.__selfTriggered||(t.el.multiple?(e=t.getSelectedProperties("idx"),e=function(e,t){for(var n,i=[],o=e.slice(0),r=0;rn?(c.addClass(this.container,"inverted"),this.isInverted=!0):(c.removeClass(this.container,"inverted"),this.isInverted=!1),this.optsRect=c.rect(this.tree)},l.prototype.getOptionByIndex=function(e){return this.options[e]},l.prototype.getOptionByValue=function(e){for(var t=!1,n=0,i=this.options.length;nthis.limit&&1a.Oq?((0,g.addClassName)((n=this).frame,"busy"),n.dom.busyContent.innerText=t,setTimeout(function(){e(),(0,g.removeClassName)(n.frame,"busy"),n.dom.busyContent.innerText=""},100)):e()},t.validate=n.validate,t._renderErrors=n._renderErrors,[{mode:"preview",mixin:t,data:"json"}])},2915:function(e,t,n){"use strict";function i(e,t,n,i){var s=Array.isArray(t)?(0,a.getChildPaths)(t):[""],l=i&&i.path&&(0,a.contains)(s,i.path)?i.path:s[0],c=i&&i.direction||"asc",t='
    '+(0,r.Tl)("sort")+"
    "+(0,r.Tl)("sortFieldLabel")+'
    '+(0,r.Tl)("sortDirectionLabel")+'
    ';o()({parent:e,content:t,overlayClass:"jsoneditor-modal-overlay",overlayStyles:{backgroundColor:"rgb(1,1,1)",opacity:.3},modalClass:"jsoneditor-modal jsoneditor-modal-sort"}).afterCreate(function(e){function t(e){a.value=e,a.className="jsoneditor-button-group jsoneditor-button-group-value-"+a.value}var i=e.modalElem().querySelector("form"),o=e.modalElem().querySelector("#ok"),r=e.modalElem().querySelector("#field"),a=e.modalElem().querySelector("#direction");s.forEach(function(e){var t,n=document.createElement("option");n.text=""===(t=e)?"@":"."===t[0]?t.slice(1):t,n.value=e,r.appendChild(n)}),r.value=l||s[0],t(c||"asc"),a.onclick=function(e){t(e.target.getAttribute("data-value"))},o.onclick=function(t){t.preventDefault(),t.stopPropagation(),e.close(),n({path:r.value,direction:a.value})},i&&(i.onsubmit=o.onclick)}).afterClose(function(e){e.destroy()}).show()}n.r(t),n.d(t,{showSortModal:function(){return i}});var t=n(8340),o=n.n(t),r=n(3057),a=n(6237)},5609:function(e,t,n){"use strict";function i(e){return(i="function"==typeof Symbol&&"symbol"==_typeof(Symbol.iterator)?function(e){return void 0===e?"undefined":_typeof(e)}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":void 0===e?"undefined":_typeof(e)})(e)}function o(e,t,n,r){if("boolean"==typeof e||e instanceof Boolean||null===e||"number"==typeof e||e instanceof Number||"string"==typeof e||e instanceof String||e instanceof Date)return JSON.stringify(e);if(Array.isArray(e)){for(var a=e,s=t,l=n,c=r,d=s?l+s:void 0,u=s?"[\n":"[",h=0;hc)return u+"..."}return u+=s?"\n"+l+"]":"]"}if(e&&"object"===i(e)){var f,g=e,m=t,l=n,y=r,v=m?l+m:void 0,b=!0,C=m?"{\n":"{";if("function"==typeof g.toJSON)return o(g.toJSON(),m,l,y);for(f in g)if(function(e,t){return Object.prototype.hasOwnProperty.call(e,t)}(g,f)){var x=g[f];if(b?b=!1:C+=m?",\n":",",(C=(C+=m?v+'"'+f+'": ':'"'+f+'":')+o(x,m,v,y)).length>y)return C+"..."}return C+=m?"\n"+l+"}":"}"}}function r(e,t){for(var n="";0
    '+(0,c.Tl)("transform")+"

    "+i+'

    '+(0,c.Tl)("transformWizardLabel")+'
    '+(0,c.Tl)("transformWizardFilter")+'
    '+(0,c.Tl)("transformWizardSortBy")+'
    '+(0,c.Tl)("transformWizardSelectFields")+'
    '+(0,c.Tl)("transformQueryLabel")+'
    '+(0,c.Tl)("transformPreviewLabel")+'
    ';s()({parent:t,content:e,overlayClass:"jsoneditor-modal-overlay",overlayStyles:{backgroundColor:"rgb(1,1,1)",opacity:.3},modalClass:"jsoneditor-modal jsoneditor-modal-transform",focus:!1}).afterCreate(function(e){function t(e){return""===e?"@":"."===e[0]?e.slice(1):e}function i(e,t){try{w.value=a(e,t),m.disabled=!1,E()}catch(t){e='Error: an error happened when executing "createQuery": '+(t.message||t.toString()),w.value="",m.disabled=!0,A.className="jsoneditor-transform-preview jsoneditor-error",A.value=e}}function s(){var e={};if(y.value&&v.value&&b.value&&(e.filter={field:y.value,relation:v.value,value:b.value}),C.value&&x.value&&(e.sort={field:C.value,direction:x.value}),I.value){for(var t,o=[],r=0;ri?(t=n,("number"==typeof(a=i)?t.slice(0,a):t)+"..."):n),m.disabled=!1}catch(e){A.className="jsoneditor-transform-preview jsoneditor-error",A.value=e.toString(),m.disabled=!0}var t,n,i,a},300);w.oninput=E,m.onclick=function(t){t.preventDefault(),t.stopPropagation(),e.close(),f(w.value)},i(n,{}),setTimeout(function(){w.select(),w.focus(),w.selectionStart=3,w.selectionEnd=3})}).afterClose(function(e){e.destroy()}).show()}n.r(t),n.d(t,{showTransformModal:function(){return a}});var t=n(8340),s=n.n(t),t=n(4772),l=n.n(t),c=n(3057),d=n(6237),u=n(660),h='Enter a JMESPath query to filter, sort, or transform the JSON data.
    To learn JMESPath, go to the interactive tutorial.'},1948:function(e,t,n){"use strict";function i(e){return(i="function"==typeof Symbol&&"symbol"==_typeof(Symbol.iterator)?function(e){return void 0===e?"undefined":_typeof(e)}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":void 0===e?"undefined":_typeof(e)})(e)}function o(e,t){for(var n=0;n=a[e].key.column&&n.column<=a[e].keyEnd.column&&(i=e.slice(0,e.lastIndexOf("/"))), -(i=(null==(o=a[e].value)?void 0:o.line)===n.row&&(null==(o=a[e].value)?void 0:o.line)===(null==(o=a[e].valueEnd)?void 0:o.line)&&n.column>=a[e].value.column&&n.column<=a[e].valueEnd.column?e:i)&&(o=t(i,r.suggestions,""),s(o))})})}catch(e){}}}])&&o(t.prototype,n),Object.defineProperty(t,"prototype",{writable:!1}),e}(),t={},x="ace/theme/jsoneditor";t.create=function(e){var t,n,i,o=this,a=1"),line:e}]),this._renderErrors(i),"function"==typeof this.options.onValidationError&&(0,b.isValidationErrorChanged)(i,this.lastSchemaErrors)&&this.options.onValidationError.call(this,i),this.lastSchemaErrors=i,Promise.resolve(this.lastSchemaErrors)}},t._validateAndCatch=function(){this.validate().catch(function(e){console.error("Error running validation:",e)})},t._renderErrors=function(e){var t=this.getText(),n=[],t=(e.reduce(function(e,t){return"string"==typeof t.dataPath&&-1===e.indexOf(t.dataPath)&&e.push(t.dataPath),e},n),(0,b.getPositionForPath)(t,n));this.aceEditor&&(this.annotations=t.map(function(t){var n=e.filter(function(e){return e.dataPath===t.path}),i=n.map(function(e){return e.message}).join("\n");return i?{row:t.line,column:t.column,text:"Schema validation error"+(1!==n.length?"s":"")+": \n"+i,type:"warning",source:"jsoneditor"}:{}}),this._refreshAnnotations()),this.errorTable.setErrors(e,t),this.aceEditor&&this.aceEditor.resize(!1)},t.getTextSelection=function(){var e,t,n,i={};return this.textarea?(n=(0,b.getInputSelection)(this.textarea),this.cursorInfo&&this.cursorInfo.line===n.end.row&&this.cursorInfo.column===n.end.column?(i.start=n.end,i.end=n.start):i=n,{start:i.start,end:i.end,text:this.textarea.value.substring(n.startIndex,n.endIndex)}):this.aceEditor?(n=this.aceEditor.getSelection(),e=this.aceEditor.getSelectedText(),t=n.getRange(),(n=n.getSelectionLead()).row===t.end.row&&n.column===t.end.column?i=t:(i.start=t.end,i.end=t.start),{start:{row:i.start.row+1,column:i.start.column+1},end:{row:i.end.row+1,column:i.end.column+1},text:e}):void 0},t.onTextSelectionChange=function(e){"function"==typeof e&&(this._selectionChangedHandler=(0,b.debounce)(e,this.DEBOUNCE_INTERVAL))},t.setTextSelection=function(e,t){var n,i,o;e&&t&&(this.textarea?(n=(0,b.getIndexForPosition)(this.textarea,e.row,e.column),o=(0,b.getIndexForPosition)(this.textarea,t.row,t.column),-1this.textarea.clientHeight?i-this.textarea.clientHeight/2:0)):this.aceEditor&&(o={start:{row:e.row-1,column:e.column-1},end:{row:t.row-1,column:t.column-1}},this.aceEditor.selection.setRange(o),this.aceEditor.scrollToLine(e.row-1,!0)))};var I=[{mode:"text",mixin:t,data:"text",load:a},{mode:"code",mixin:t,data:"text",load:a}]},5633:function(e,t,n){"use strict";function i(e){return(i="function"==typeof Symbol&&"symbol"==_typeof(Symbol.iterator)?function(e){return void 0===e?"undefined":_typeof(e)}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":void 0===e?"undefined":_typeof(e)})(e)}function o(e,t){for(var n=0;n=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:t};throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function l(e,t){var n;if(e)return"string"==typeof e?c(e,t):"Map"===(n="Object"===(n=Object.prototype.toString.call(e).slice(8,-1))&&e.constructor?e.constructor.name:n)||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?c(e,t):void 0}function c(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,i=new Array(t);n=t.length;o--)this.removeChild(this.childs[o],!1)}else if("object"===this.type){for(this.childs||(this.childs=[]),o=this.childs.length-1;0<=o;o--)f(t,this.childs[o].field)||this.removeChild(this.childs[o],!1);for(var d in i=0,t)f(t,d)&&(void 0===(s=t[d])||s instanceof Function||((l=this.findChildByProperty(d))?(l.setField(d,!0),l.setValue(s)):(l=new e(this.editor,{field:d,value:s}),d=i=t.childs.length;a--)this.removeChild(this.childs[a],!1)}else if("object"===t.type){for(this.childs||(this.childs=[]),r=0;r=t.childs.length;a--)this.removeChild(this.childs[a],!1)}else this.hideChilds(),delete this.append,delete this.showMore,delete this.expanded,delete this.childs,this.value=t.value;Array.isArray(s)!==Array.isArray(this.childs)&&this.recreateDom(),this.updateDom({updateIndexes:!0}),this.previousValue=this.value}},{key:"recreateDom",value:function(){var e;this.dom&&this.dom.tr&&this.dom.tr.parentNode?(e=this._detachFromDom(),this.clearDom(),this._attachToDom(e)):this.clearDom()}},{key:"getValue",value:function(){var e,t;return"array"===this.type?(e=[],this.childs.forEach(function(t){e.push(t.getValue())}),e):"object"===this.type?(t={},this.childs.forEach(function(e){t[e.getField()]=e.getValue()}),t):(void 0===this.value&&this._getDomValue(),this.value)}},{key:"getInternalValue",value:function(){return"array"===this.type?{type:this.type,childs:this.childs.map(function(e){return e.getInternalValue()})}:"object"===this.type?{type:this.type,childs:this.childs.map(function(e){return{field:e.getField(),value:e.getInternalValue()}})}:(void 0===this.value&&this._getDomValue(),{type:this.type,value:this.value})}},{key:"getLevel",value:function(){return this.parent?this.parent.getLevel()+1:0}},{key:"getNodePath",value:function(){var e=this.parent?this.parent.getNodePath():[];return e.push(this),e}},{key:"clone",value:function(){var t,n;return(t=new e(this.editor)).type=this.type,t.field=this.field,t.fieldInnerText=this.fieldInnerText, -t.fieldEditable=this.fieldEditable,t.previousField=this.previousField,t.value=this.value,t.valueInnerText=this.valueInnerText,t.previousValue=this.previousValue,t.expanded=this.expanded,t.visibleChilds=this.visibleChilds,this.childs?(n=[],this.childs.forEach(function(e){e=e.clone(),e.setParent(t),n.push(e)}),t.childs=n):t.childs=void 0,t}},{key:"expand",value:function(e){this.childs&&(this.expanded=!0,this.dom.expand&&(this.dom.expand.className="jsoneditor-button jsoneditor-expanded"),this.showChilds(),!1!==e&&this.childs.forEach(function(t){t.expand(e)}),this.updateDom({recurse:!1}))}},{key:"collapse",value:function(e){this.childs&&(this.hideChilds(),!1!==e&&this.childs.forEach(function(t){t.collapse(e)}),this.dom.expand&&(this.dom.expand.className="jsoneditor-button jsoneditor-collapsed"),this.expanded=!1,this.updateDom({recurse:!1}))}},{key:"showChilds",value:function(){var e=this.childs;if(e&&this.expanded){var e=this.dom.tr,t=e?e.parentNode:void 0;if(t){for(var n=this.getAppendDom(),i=(n.parentNode||((o=e.nextSibling)?t.insertBefore(n,o):t.appendChild(n)),Math.min(this.childs.length,this.visibleChilds)),o=this._getNextTr(),r=0;rthis.visibleChilds?(r=this.childs[this.visibleChilds-1],this.insertBefore(e,r,n)):this.appendChild(e,!0,n):this.insertBefore(e,t,n),i)&&o&&i.removeChild(o)}},{key:"insertBefore",value:function(e,t,n){if(this._hasChilds()){if(this.visibleChilds++,"object"===this.type&&void 0===e.field&&e.setField(""),t===this.append)e.setParent(this),e.fieldEditable="object"===this.type,this.childs.push(e);else{var i=this.childs.indexOf(t);if(-1===i)throw new Error("Node not found");e.setParent(this),e.fieldEditable="object"===this.type,this.childs.splice(i,0,e)}var o;this.expanded&&(i=e.getDom(),o=(t=t.getDom())?t.parentNode:void 0,t&&o&&o.insertBefore(i,t),e.showChilds(),this.showChilds()),!1!==n&&(this.updateDom({updateIndexes:!0}),e.updateDom({recurse:!0}))}}},{key:"insertAfter",value:function(e,t){this._hasChilds()&&(t=this.childs.indexOf(t),(t=this.childs[t+1])?this.insertBefore(e,t):this.appendChild(e))}},{key:"search",value:function(e,t){Array.isArray(t)||(t=[]);var n=e?e.toLowerCase():void 0;return delete this.searchField,delete this.searchValue,void 0!==this.field&&t.length<=this.MAX_SEARCH_RESULTS&&(-1!==String(this.field).toLowerCase().indexOf(n)&&(this.searchField=!0,t.push({node:this,elem:"field"})),this._updateDomField()),this._hasChilds()?this.childs&&this.childs.forEach(function(n){n.search(e,t)}):void 0!==this.value&&t.length<=this.MAX_SEARCH_RESULTS&&(-1!==String(this.value).toLowerCase().indexOf(n)&&(this.searchValue=!0,t.push({node:this,elem:"value"})),this._updateDomValue()),t}},{key:"scrollTo",value:function(e){this.expandPathToNode(),this.dom.tr&&this.dom.tr.parentNode&&this.editor.scrollTo(this.dom.tr.offsetTop,e)}},{key:"expandPathToNode",value:function(){for(var e=this;e&&e.parent;){for(var t="array"===e.parent.type?e.index:e.parent.childs.indexOf(e);e.parent.visibleChilds/g,">").replace(/ {2}/g,"  ").replace(/^ /," ").replace(/ $/," "),e=(e=JSON.stringify(e)).substring(1,e.length-1),!0===this.editor.options.escapeUnicode?(0,L.escapeUnicodeChars)(e):e)}},{key:"_unescapeHTML",value:function(e){return e='"'+this._escapeJSON(e)+'"',(0,L.parse)(e).replace(/</g,"<").replace(/>/g,">").replace(/ |\u00A0/g," ").replace(/&/g,"&")}},{key:"_escapeJSON",value:function(e){for(var t="",n=0;nthis.parent.visibleChilds},m.prototype.onEvent=function(e){"keydown"===e.type&&this.onKeyDown(e)};var F=m,P=function(){function e(t){function n(e){return t.node.findNodeByInternalPath(e)}if(!(this instanceof e))throw new TypeError("Cannot call a class as a function");this.editor=t,this.history=[],this.index=-1,this.clear(),this.actions={editField:{undo:function(e){n(e.parentPath).childs[e.index].updateField(e.oldValue)},redo:function(e){n(e.parentPath).childs[e.index].updateField(e.newValue)}},editValue:{undo:function(e){n(e.path).updateValue(e.oldValue)},redo:function(e){n(e.path).updateValue(e.newValue)}},changeType:{undo:function(e){n(e.path).changeType(e.oldType)},redo:function(e){n(e.path).changeType(e.newType)}},appendNodes:{undo:function(e){var t=n(e.parentPath);e.paths.map(n).forEach(function(e){t.removeChild(e)})},redo:function(e){var t=n(e.parentPath);e.nodes.forEach(function(e){t.appendChild(e)})}},insertBeforeNodes:{undo:function(e){var t=n(e.parentPath);e.paths.map(n).forEach(function(e){t.removeChild(e)})},redo:function(e){var t=n(e.parentPath),i=n(e.beforePath);e.nodes.forEach(function(e){t.insertBefore(e,i)})}},insertAfterNodes:{undo:function(e){var t=n(e.parentPath);e.paths.map(n).forEach(function(e){t.removeChild(e)})},redo:function(e){var t=n(e.parentPath),i=n(e.afterPath);e.nodes.forEach(function(e){t.insertAfter(e,i),i=e})}},removeNodes:{undo:function(e){var t=n(e.parentPath),i=t.childs[e.index]||t.append;e.nodes.forEach(function(e){t.insertBefore(e,i)})},redo:function(e){var t=n(e.parentPath);e.paths.map(n).forEach(function(e){t.removeChild(e)})}},duplicateNodes:{undo:function(e){var t=n(e.parentPath);e.clonePaths.map(n).forEach(function(e){t.removeChild(e)})},redo:function(e){var t=n(e.parentPath),i=n(e.afterPath);e.paths.map(n).forEach(function(e){var n,o=e.clone();"object"===t.type&&(n=t.getFieldNames(),o.field=(0,L.findUniqueName)(e.field,n)),t.insertAfter(o,i),i=o})}},moveNodes:{undo:function(e){var t=n(e.oldParentPath),i=n(e.newParentPath),o=t.childs[e.oldIndex]||t.append;i.childs.slice(e.newIndex,e.newIndex+e.count).forEach(function(n,i){n.field=e.fieldNames[i],t.moveBefore(n,o)}),null===e.newParentPathRedo&&(e.newParentPathRedo=i.getInternalPath())},redo:function(e){var t=n(e.oldParentPathRedo),i=n(e.newParentPathRedo),o=i.childs[e.newIndexRedo]||i.append;t.childs.slice(e.oldIndexRedo,e.oldIndexRedo+e.count).forEach(function(t,n){t.field=e.fieldNames[n],i.moveBefore(t,o)})}},sort:{undo:function(e){var t=n(e.path);t.hideChilds(),t.childs=e.oldChilds,t.updateDom({updateIndexes:!0}),t.showChilds()},redo:function(e){var t=n(e.path);t.hideChilds(),t.childs=e.newChilds,t.updateDom({updateIndexes:!0}),t.showChilds()}},transform:{undo:function(e){n(e.path).setInternalValue(e.oldValue)},redo:function(e){n(e.path).setInternalValue(e.newValue)}}}}var t,n;return t=e,(n=[{key:"onChange",value:function(){}},{key:"add",value:function(e,t){this.index++,this.history[this.index]={action:e,params:t,timestamp:new Date},this.indexthis.results.length-1&&(t=0),this._setActiveResult(t,e))}},{key:"previous",value:function(e){var t,n;this.results&&(t=this.results.length-1,n=null!==this.resultIndex?this.resultIndex-1:t,this._setActiveResult(n=n<0?t:n,e))}},{key:"_setActiveResult",value:function(e,t){var n,i,o;this.activeResult&&(n=this.activeResult.node,"field"===this.activeResult.elem?delete n.searchFieldActive:delete n.searchValueActive,n.updateDom()),this.results&&this.results[e]?(this.resultIndex=e,i=this.results[this.resultIndex].node,"field"===(o=this.results[this.resultIndex].elem)?i.searchFieldActive=!0:i.searchValueActive=!0,this.activeResult=this.results[this.resultIndex],i.updateDom(),i.scrollTo(function(){t&&i.focus(o)})):(this.resultIndex=void 0,this.activeResult=void 0)}},{key:"_clearDelay",value:function(){void 0!==this.timeout&&(clearTimeout(this.timeout),delete this.timeout)}},{key:"_onDelayedSearch",value:function(e){this._clearDelay();var t=this;this.timeout=setTimeout(function(e){t._onSearch()},this.delay)}},{key:"_onSearch",value:function(e){this._clearDelay();var t=this.dom.search.value,t=0=a[e].value.column&&n.column<=a[e].valueEnd.column?e:i)&&(o=t(i,r.suggestions,""),s(o))})})}catch(e){}}}])&&o(t.prototype,n),Object.defineProperty(t,"prototype",{writable:!1}),e}(),t={},x="ace/theme/jsoneditor";t.create=function(e){var t,n,i,o=this,a=1"),line:e}]),this._renderErrors(i),"function"==typeof this.options.onValidationError&&(0,b.isValidationErrorChanged)(i,this.lastSchemaErrors)&&this.options.onValidationError.call(this,i),this.lastSchemaErrors=i,Promise.resolve(this.lastSchemaErrors)}},t._validateAndCatch=function(){this.validate().catch(function(e){console.error("Error running validation:",e)})},t._renderErrors=function(e){var t=this.getText(),n=[],t=(e.reduce(function(e,t){return"string"==typeof t.dataPath&&-1===e.indexOf(t.dataPath)&&e.push(t.dataPath),e},n),(0,b.getPositionForPath)(t,n));this.aceEditor&&(this.annotations=t.map(function(t){var n=e.filter(function(e){return e.dataPath===t.path}),i=n.map(function(e){return e.message}).join("\n");return i?{row:t.line,column:t.column,text:"Schema validation error"+(1!==n.length?"s":"")+": \n"+i,type:"warning",source:"jsoneditor"}:{}}),this._refreshAnnotations()),this.errorTable.setErrors(e,t),this.aceEditor&&this.aceEditor.resize(!1)},t.getTextSelection=function(){var e,t,n,i={};return this.textarea?(n=(0,b.getInputSelection)(this.textarea),this.cursorInfo&&this.cursorInfo.line===n.end.row&&this.cursorInfo.column===n.end.column?(i.start=n.end,i.end=n.start):i=n,{start:i.start,end:i.end,text:this.textarea.value.substring(n.startIndex,n.endIndex)}):this.aceEditor?(n=this.aceEditor.getSelection(),e=this.aceEditor.getSelectedText(),t=n.getRange(),(n=n.getSelectionLead()).row===t.end.row&&n.column===t.end.column?i=t:(i.start=t.end,i.end=t.start),{start:{row:i.start.row+1,column:i.start.column+1},end:{row:i.end.row+1,column:i.end.column+1},text:e}):void 0},t.onTextSelectionChange=function(e){"function"==typeof e&&(this._selectionChangedHandler=(0,b.debounce)(e,this.DEBOUNCE_INTERVAL))},t.setTextSelection=function(e,t){var n,i,o;e&&t&&(this.textarea?(n=(0,b.getIndexForPosition)(this.textarea,e.row,e.column),o=(0,b.getIndexForPosition)(this.textarea,t.row,t.column),-1this.textarea.clientHeight?i-this.textarea.clientHeight/2:0)):this.aceEditor&&(o={start:{row:e.row-1,column:e.column-1},end:{row:t.row-1,column:t.column-1}},this.aceEditor.selection.setRange(o),this.aceEditor.scrollToLine(e.row-1,!0)))};var I=[{mode:"text",mixin:t,data:"text",load:a},{mode:"code",mixin:t,data:"text",load:a}]},5633:function(e,t,n){"use strict";function i(e){return(i="function"==typeof Symbol&&"symbol"==_typeof(Symbol.iterator)?function(e){return void 0===e?"undefined":_typeof(e)}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":void 0===e?"undefined":_typeof(e)})(e)}function o(e,t){for(var n=0;n=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:t};throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function l(e,t){var n;if(e)return"string"==typeof e?c(e,t):"Map"===(n="Object"===(n=Object.prototype.toString.call(e).slice(8,-1))&&e.constructor?e.constructor.name:n)||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?c(e,t):void 0}function c(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,i=new Array(t);n=t.length;o--)this.removeChild(this.childs[o],!1)}else if("object"===this.type){for(this.childs||(this.childs=[]),o=this.childs.length-1;0<=o;o--)f(t,this.childs[o].field)||this.removeChild(this.childs[o],!1);for(var d in i=0,t)f(t,d)&&(void 0===(s=t[d])||s instanceof Function||((l=this.findChildByProperty(d))?(l.setField(d,!0),l.setValue(s)):(l=new e(this.editor,{field:d,value:s}),d=i=t.childs.length;a--)this.removeChild(this.childs[a],!1)}else if("object"===t.type){for(this.childs||(this.childs=[]),r=0;r=t.childs.length;a--)this.removeChild(this.childs[a],!1)}else this.hideChilds(),delete this.append,delete this.showMore,delete this.expanded,delete this.childs,this.value=t.value;Array.isArray(s)!==Array.isArray(this.childs)&&this.recreateDom(),this.updateDom({updateIndexes:!0}),this.previousValue=this.value}},{key:"recreateDom",value:function(){var e;this.dom&&this.dom.tr&&this.dom.tr.parentNode?(e=this._detachFromDom(),this.clearDom(),this._attachToDom(e)):this.clearDom()}},{key:"getValue",value:function(){var e,t;return"array"===this.type?(e=[],this.childs.forEach(function(t){e.push(t.getValue())}),e):"object"===this.type?(t={},this.childs.forEach(function(e){t[e.getField()]=e.getValue()}),t):(void 0===this.value&&this._getDomValue(),this.value)}},{key:"getInternalValue",value:function(){return"array"===this.type?{type:this.type,childs:this.childs.map(function(e){return e.getInternalValue()})}:"object"===this.type?{type:this.type,childs:this.childs.map(function(e){return{field:e.getField(),value:e.getInternalValue()}})}:(void 0===this.value&&this._getDomValue(),{type:this.type,value:this.value})}},{key:"getLevel",value:function(){return this.parent?this.parent.getLevel()+1:0}},{key:"getNodePath",value:function(){var e=this.parent?this.parent.getNodePath():[];return e.push(this),e}},{key:"clone",value:function(){var t,n;return(t=new e(this.editor)).type=this.type,t.field=this.field,t.fieldInnerText=this.fieldInnerText, +t.fieldEditable=this.fieldEditable,t.previousField=this.previousField,t.value=this.value,t.valueInnerText=this.valueInnerText,t.previousValue=this.previousValue,t.expanded=this.expanded,t.visibleChilds=this.visibleChilds,this.childs?(n=[],this.childs.forEach(function(e){e=e.clone(),e.setParent(t),n.push(e)}),t.childs=n):t.childs=void 0,t}},{key:"expand",value:function(e){this.childs&&(this.expanded=!0,this.dom.expand&&(this.dom.expand.className="jsoneditor-button jsoneditor-expanded"),this.showChilds(),!1!==e&&this.childs.forEach(function(t){t.expand(e)}),this.updateDom({recurse:!1}))}},{key:"collapse",value:function(e){this.childs&&(this.hideChilds(),!1!==e&&this.childs.forEach(function(t){t.collapse(e)}),this.dom.expand&&(this.dom.expand.className="jsoneditor-button jsoneditor-collapsed"),this.expanded=!1,this.updateDom({recurse:!1}))}},{key:"showChilds",value:function(){var e=this.childs;if(e&&this.expanded){var e=this.dom.tr,t=e?e.parentNode:void 0;if(t){for(var n=this.getAppendDom(),i=(n.parentNode||((o=e.nextSibling)?t.insertBefore(n,o):t.appendChild(n)),Math.min(this.childs.length,this.visibleChilds)),o=this._getNextTr(),r=0;rthis.visibleChilds?(r=this.childs[this.visibleChilds-1],this.insertBefore(e,r,n)):this.appendChild(e,!0,n):this.insertBefore(e,t,n),i)&&o&&i.removeChild(o)}},{key:"insertBefore",value:function(e,t,n){if(this._hasChilds()){if(this.visibleChilds++,"object"===this.type&&void 0===e.field&&e.setField(""),t===this.append)e.setParent(this),e.fieldEditable="object"===this.type,this.childs.push(e);else{var i=this.childs.indexOf(t);if(-1===i)throw new Error("Node not found");e.setParent(this),e.fieldEditable="object"===this.type,this.childs.splice(i,0,e)}var o;this.expanded&&(i=e.getDom(),o=(t=t.getDom())?t.parentNode:void 0,t&&o&&o.insertBefore(i,t),e.showChilds(),this.showChilds()),!1!==n&&(this.updateDom({updateIndexes:!0}),e.updateDom({recurse:!0}))}}},{key:"insertAfter",value:function(e,t){this._hasChilds()&&(t=this.childs.indexOf(t),(t=this.childs[t+1])?this.insertBefore(e,t):this.appendChild(e))}},{key:"search",value:function(e,t){Array.isArray(t)||(t=[]);var n=e?e.toLowerCase():void 0;return delete this.searchField,delete this.searchValue,void 0!==this.field&&t.length<=this.MAX_SEARCH_RESULTS&&(-1!==String(this.field).toLowerCase().indexOf(n)&&(this.searchField=!0,t.push({node:this,elem:"field"})),this._updateDomField()),this._hasChilds()?this.childs&&this.childs.forEach(function(n){n.search(e,t)}):void 0!==this.value&&t.length<=this.MAX_SEARCH_RESULTS&&(-1!==String(this.value).toLowerCase().indexOf(n)&&(this.searchValue=!0,t.push({node:this,elem:"value"})),this._updateDomValue()),t}},{key:"scrollTo",value:function(e){this.expandPathToNode(),this.dom.tr&&this.dom.tr.parentNode&&this.editor.scrollTo(this.dom.tr.offsetTop,e)}},{key:"expandPathToNode",value:function(){for(var e=this;e&&e.parent;){for(var t="array"===e.parent.type?e.index:e.parent.childs.indexOf(e);e.parent.visibleChilds/g,">").replace(/ {2}/g,"  ").replace(/^ /," ").replace(/ $/," "),e=(e=JSON.stringify(e)).substring(1,e.length-1),!0===this.editor.options.escapeUnicode?(0,L.escapeUnicodeChars)(e):e)}},{key:"_unescapeHTML",value:function(e){return e='"'+this._escapeJSON(e)+'"',(0,L.parse)(e).replace(/</g,"<").replace(/>/g,">").replace(/ |\u00A0/g," ").replace(/&/g,"&")}},{key:"_escapeJSON",value:function(e){for(var t="",n=0;nthis.parent.visibleChilds},m.prototype.onEvent=function(e){"keydown"===e.type&&this.onKeyDown(e)};var B=m,P=function(){function e(t){function n(e){return t.node.findNodeByInternalPath(e)}if(!(this instanceof e))throw new TypeError("Cannot call a class as a function");this.editor=t,this.history=[],this.index=-1,this.clear(),this.actions={editField:{undo:function(e){n(e.parentPath).childs[e.index].updateField(e.oldValue)},redo:function(e){n(e.parentPath).childs[e.index].updateField(e.newValue)}},editValue:{undo:function(e){n(e.path).updateValue(e.oldValue)},redo:function(e){n(e.path).updateValue(e.newValue)}},changeType:{undo:function(e){n(e.path).changeType(e.oldType)},redo:function(e){n(e.path).changeType(e.newType)}},appendNodes:{undo:function(e){var t=n(e.parentPath);e.paths.map(n).forEach(function(e){t.removeChild(e)})},redo:function(e){var t=n(e.parentPath);e.nodes.forEach(function(e){t.appendChild(e)})}},insertBeforeNodes:{undo:function(e){var t=n(e.parentPath);e.paths.map(n).forEach(function(e){t.removeChild(e)})},redo:function(e){var t=n(e.parentPath),i=n(e.beforePath);e.nodes.forEach(function(e){t.insertBefore(e,i)})}},insertAfterNodes:{undo:function(e){var t=n(e.parentPath);e.paths.map(n).forEach(function(e){t.removeChild(e)})},redo:function(e){var t=n(e.parentPath),i=n(e.afterPath);e.nodes.forEach(function(e){t.insertAfter(e,i),i=e})}},removeNodes:{undo:function(e){var t=n(e.parentPath),i=t.childs[e.index]||t.append;e.nodes.forEach(function(e){t.insertBefore(e,i)})},redo:function(e){var t=n(e.parentPath);e.paths.map(n).forEach(function(e){t.removeChild(e)})}},duplicateNodes:{undo:function(e){var t=n(e.parentPath);e.clonePaths.map(n).forEach(function(e){t.removeChild(e)})},redo:function(e){var t=n(e.parentPath),i=n(e.afterPath);e.paths.map(n).forEach(function(e){var n,o=e.clone();"object"===t.type&&(n=t.getFieldNames(),o.field=(0,L.findUniqueName)(e.field,n)),t.insertAfter(o,i),i=o})}},moveNodes:{undo:function(e){var t=n(e.oldParentPath),i=n(e.newParentPath),o=t.childs[e.oldIndex]||t.append;i.childs.slice(e.newIndex,e.newIndex+e.count).forEach(function(n,i){n.field=e.fieldNames[i],t.moveBefore(n,o)}),null===e.newParentPathRedo&&(e.newParentPathRedo=i.getInternalPath())},redo:function(e){var t=n(e.oldParentPathRedo),i=n(e.newParentPathRedo),o=i.childs[e.newIndexRedo]||i.append;t.childs.slice(e.oldIndexRedo,e.oldIndexRedo+e.count).forEach(function(t,n){t.field=e.fieldNames[n],i.moveBefore(t,o)})}},sort:{undo:function(e){var t=n(e.path);t.hideChilds(),t.childs=e.oldChilds,t.updateDom({updateIndexes:!0}),t.showChilds()},redo:function(e){var t=n(e.path);t.hideChilds(),t.childs=e.newChilds,t.updateDom({updateIndexes:!0}),t.showChilds()}},transform:{undo:function(e){n(e.path).setInternalValue(e.oldValue)},redo:function(e){n(e.path).setInternalValue(e.newValue)}}}}var t,n;return t=e,(n=[{key:"onChange",value:function(){}},{key:"add",value:function(e,t){this.index++,this.history[this.index]={action:e,params:t,timestamp:new Date},this.indexthis.results.length-1&&(t=0),this._setActiveResult(t,e))}},{key:"previous",value:function(e){var t,n;this.results&&(t=this.results.length-1,n=null!==this.resultIndex?this.resultIndex-1:t,this._setActiveResult(n=n<0?t:n,e))}},{key:"_setActiveResult",value:function(e,t){var n,i,o;this.activeResult&&(n=this.activeResult.node,"field"===this.activeResult.elem?delete n.searchFieldActive:delete n.searchValueActive,n.updateDom()),this.results&&this.results[e]?(this.resultIndex=e,i=this.results[this.resultIndex].node,"field"===(o=this.results[this.resultIndex].elem)?i.searchFieldActive=!0:i.searchValueActive=!0,this.activeResult=this.results[this.resultIndex],i.updateDom(),i.scrollTo(function(){t&&i.focus(o)})):(this.resultIndex=void 0,this.activeResult=void 0)}},{key:"_clearDelay",value:function(){void 0!==this.timeout&&(clearTimeout(this.timeout),delete this.timeout)}},{key:"_onDelayedSearch",value:function(e){this._clearDelay();var t=this;this.timeout=setTimeout(function(e){t._onSearch()},this.delay)}},{key:"_onSearch",value:function(e){this._clearDelay();var t=this.dom.search.value,t=0e.length)&&(t=e.length);for(var n=0,i=new Array(t);n=e.left&&t.right+n<=e.right&&t.top-n>=e.top&&t.bottom+n<=e.bottom}function W(e,t,n){var i;return function(){var o=this,r=arguments,a=n&&!i;clearTimeout(i),i=setTimeout(function(){i=null,n||e.apply(o,r)},t),a&&e.apply(o,r)}}function V(e,t){for(var n=t.length,i=0,o=e.length,r=t.length;t.charAt(i)===e.charAt(i)&&ithis.length)&&(t=this.length),t-=e.length,-1!==(e=this.indexOf(e,t))&&e===t}),String.prototype.repeat||i(String.prototype,"repeat",function(e){for(var t="",n=this;0>=1)&&(n+=n);return t}),String.prototype.includes||i(String.prototype,"includes",function(e,t){return-1!=this.indexOf(e,t)}),Object.assign||(Object.assign=function(e){if(null==e)throw new TypeError("Cannot convert undefined or null to object");for(var t=Object(e),n=1;n>>0,n=arguments[1]>>0,i=n<0?Math.max(t+n,0):Math.min(n,t),n=arguments[2],n=void 0===n?t:n>>0,o=n<0?Math.max(t+n,0):Math.min(n,t);i>=1)&&(e+=e);return n};var i=/^\s\s*/,o=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(i,"")},t.stringTrimRight=function(e){return e.replace(o,"")},t.copyObject=function(e){var t,n={};for(t in e)n[t]=e[t];return n},t.copyArray=function(e){for(var t=[],n=0,i=e.length;n ["+this.end.row+"/"+this.end.column+"]"},i.prototype.contains=function(e,t){return 0==this.compare(e,t)},i.prototype.compareRange=function(e){var t=e.end,e=e.start,t=this.compare(t.row,t.column);return 1==t?1==(t=this.compare(e.row,e.column))?2:0==t?1:0:-1==t?-2:-1==(t=this.compare(e.row,e.column))?-1:1==t?42:0},i.prototype.comparePoint=function(e){return this.compare(e.row,e.column)},i.prototype.containsRange=function(e){return 0==this.comparePoint(e.start)&&0==this.comparePoint(e.end)},i.prototype.intersects=function(e){return-1==(e=this.compareRange(e))||0==e||1==e},i.prototype.isEnd=function(e,t){return this.end.row==e&&this.end.column==t},i.prototype.isStart=function(e,t){return this.start.row==e&&this.start.column==t},i.prototype.setStart=function(e,t){"object"==(void 0===e?"undefined":_typeof(e))?(this.start.column=e.column,this.start.row=e.row):(this.start.row=e,this.start.column=t)},i.prototype.setEnd=function(e,t){"object"==(void 0===e?"undefined":_typeof(e))?(this.end.column=e.column,this.end.row=e.row):(this.end.row=e,this.end.column=t)},i.prototype.inside=function(e,t){return 0==this.compare(e,t)&&!this.isEnd(e,t)&&!this.isStart(e,t)},i.prototype.insideStart=function(e,t){return 0==this.compare(e,t)&&!this.isEnd(e,t)},i.prototype.insideEnd=function(e,t){return 0==this.compare(e,t)&&!this.isStart(e,t)},i.prototype.compare=function(e,t){return this.isMultiLine()||e!==this.start.row?ethis.end.row?1:this.start.row===e?t>=this.start.column?0:-1:this.end.row!==e||t<=this.end.column?0:1:tthis.end.column?1:0},i.prototype.compareStart=function(e,t){return this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},i.prototype.compareEnd=function(e,t){return this.end.row==e&&this.end.column==t?1:this.compare(e,t)},i.prototype.compareInside=function(e,t){return this.end.row==e&&this.end.column==t?1:this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},i.prototype.clipRows=function(e,t){var n,o;return this.end.row>t?n={row:t+1,column:0}:this.end.rowt?o={row:t+1,column:0}:this.start.rowDate.now()-50)||(i=!1)},cancel:function(){i=Date.now()}}}),ace.define("ace/keyboard/textinput",["require","exports","module","ace/lib/event","ace/config","ace/lib/useragent","ace/lib/dom","ace/lib/lang","ace/clipboard","ace/lib/keys"],function(e,t,n){"use strict";var i=e("../lib/event"),o=e("../config").nls,r=e("../lib/useragent"),a=e("../lib/dom"),s=e("../lib/lang"),l=e("../clipboard"),c=r.isChrome<18,d=r.isIE,u=63M?(r=t.session.getLine(M+1),i=s.end.row>M+1?r.length:s.end.column,i+=o.length+1,o=o+"\n"+r):m&&0=$.length&&e.value===$&&$&&e.selectionEnd!==j}),W=null,V=(this.setInputHandler=function(e){W=e},!(this.getInputHandler=function(){return W})),G=function(e,n){if(V=V&&!1,k)return P(),e&&t.onPaste(e),k=!1,"";for(var i=w.selectionStart,o=w.selectionEnd,a=L,s=$.length-j,l=e,c=e.length-i,d=e.length-o,u=0;0L-1&&$[$.length-u]==e[e.length-u];)u++,s--;c-=u-1,d-=u-1;var h=l.length-u+1;return h<0&&(a=-h,h=0),l=l.slice(0,h),n||l||c||a||s||d?(h=!(S=!0),r.isAndroid&&". "==l&&(l=" ",h=!0),l&&!a&&!s&&!c&&!d||E?t.onTextInput(l):t.onTextInput(l,{extendLeft:a,extendRight:s,restoreStart:c,restoreEnd:d}),S=!1,$=e,L=i,j=o,O=d,h?"\n":l):""},Z=function(e){if(_)return X();if(e&&e.inputType){if("historyUndo"==e.inputType)return t.execCommand("undo");if("historyRedo"==e.inputType)return t.execCommand("redo")}var e=w.value,n=G(e,!0);(500this.$focusTimeout)&&this.startSelect(this.mousedownEvent.getDocumentPosition())},i.prototype.onDoubleClick=function(e){var e=e.getDocumentPosition(),t=this.editor,n=t.session.getBracketRange(e);n?(n.isEmpty()&&(n.start.column--,n.end.column++),this.setState("select")):(n=t.selection.getWordRange(e.row,e.column),this.setState("selectByWords")),this.$clickSelection=n,this.select()},i.prototype.onTripleClick=function(e){var e=e.getDocumentPosition(),t=this.editor,n=(this.setState("selectByLines"),t.getSelectionRange());n.isMultiLine()&&n.contains(e.row,e.column)?(this.$clickSelection=t.selection.getLineRange(n.start.row),this.$clickSelection.end=t.selection.getLineRange(n.end.row).end):this.$clickSelection=t.selection.getLineRange(e.row),this.select()},i.prototype.onQuadClick=function(e){var t=this.editor;t.selectAll(),this.$clickSelection=t.getSelectionRange(),this.setState("selectAll")},i.prototype.onMouseWheel=function(e){var t,n,i,o,r,a,s;if(!e.getAccelKey())return e.getShiftKey()&&e.wheelY&&!e.wheelX&&(e.wheelX=e.wheelY,e.wheelY=0),t=this.editor,this.$lastScroll||(this.$lastScroll={t:0,vx:0,vy:0,allowed:0}),n=this.$lastScroll,o=(s=(i=e.domEvent.timeStamp)-n.t)?e.wheelX/s:n.vx,r=s?e.wheelY/s:n.vy,s<550&&(o=(o+n.vx)/2,r=(r+n.vy)/2),s=!1,1<=(a=Math.abs(o/r))&&t.renderer.isScrollableBy(e.wheelX*e.speed,0)&&(s=!0),(s=!!(a<=1&&t.renderer.isScrollableBy(0,e.wheelY*e.speed))||s)?n.allowed=i:i-n.allowed<550&&(Math.abs(o)<=1.5*Math.abs(n.vx)&&Math.abs(r)<=1.5*Math.abs(n.vy)?(s=!0,n.allowed=i):n.allowed=0),n.t=i,n.vx=o,n.vy=r,s?(t.renderer.scrollBy(e.wheelX*e.speed,e.wheelY*e.speed),e.stop()):void 0},i);e.prototype.selectEnd=e.prototype.selectByLinesEnd,e.prototype.selectAllEnd=e.prototype.selectByLinesEnd,e.prototype.selectByWordsEnd=e.prototype.selectByLinesEnd,t.DefaultHandlers=e}),ace.define("ace/lib/scroll",["require","exports","module"],function(e,t,n){t.preventParentScroll=function(e){e.stopPropagation();var t=e.currentTarget;t.scrollHeight>t.clientHeight||e.preventDefault()}}),ace.define("ace/tooltip",["require","exports","module","ace/lib/dom","ace/lib/event","ace/range","ace/lib/scroll"],function(e,t,n){"use strict";function i(e){this.isOpen=!1,this.$element=null,this.$parentNode=e}function o(){this.popups=[]}function r(e){void 0===e&&(e=document.body);var e=p.call(this,e)||this,t=(e.timeout=void 0,e.lastT=0,e.idleTime=350,e.lastEvent=void 0,e.onMouseOut=e.onMouseOut.bind(e),e.onMouseMove=e.onMouseMove.bind(e),e.waitForHover=e.waitForHover.bind(e),e.hide=e.hide.bind(e),e.getElement());return t.style.whiteSpace="pre-wrap",t.style.pointerEvents="auto",t.addEventListener("mouseout",e.onMouseOut),t.tabIndex=-1,t.addEventListener("blur",function(){t.contains(document.activeElement)||this.hide()}.bind(e)),t.addEventListener("wheel",u),e}var a,s=this&&this.__extends||(a=function(e,t){return(a=Object.setPrototypeOf||({__proto__:[]}instanceof Array?function(e,t){e.__proto__=t}:function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])}))(e,t)},function(e,t){function n(){this.constructor=e}if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");a(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),l=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,n=t&&e[t],i=0;if(n)return n.call(e);if(e&&"number"==typeof e.length)return{next:function(){return{value:(e=e&&i>=e.length?void 0:e)&&e[i++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},c=e("./lib/dom"),d=(e("./lib/event"),e("./range").Range),u=e("./lib/scroll").preventParentScroll,h="ace_tooltip",e=(i.prototype.$init=function(){return this.$element=c.createElement("div"),this.$element.className=h,this.$element.style.display="none",this.$parentNode.appendChild(this.$element),this.$element},i.prototype.getElement=function(){return this.$element||this.$init()},i.prototype.setText=function(e){this.getElement().textContent=e},i.prototype.setHtml=function(e){this.getElement().innerHTML=e},i.prototype.setPosition=function(e,t){this.getElement().style.left=e+"px",this.getElement().style.top=t+"px"},i.prototype.setClassName=function(e){c.addCssClass(this.getElement(),e)},i.prototype.setTheme=function(e){this.$element.className=h+" "+(e.isDark?"ace_dark ":"")+(e.cssClass||"")},i.prototype.show=function(e,t,n){null!=e&&this.setText(e),null!=t&&null!=n&&this.setPosition(t,n),this.isOpen||(this.getElement().style.display="block",this.isOpen=!0)},i.prototype.hide=function(e){this.isOpen&&(this.getElement().style.display="none",this.getElement().className=h,this.isOpen=!1)},i.prototype.getHeight=function(){return this.getElement().offsetHeight},i.prototype.getWidth=function(){return this.getElement().offsetWidth},i.prototype.destroy=function(){this.isOpen=!1,this.$element&&this.$element.parentNode&&this.$element.parentNode.removeChild(this.$element)},i);o.prototype.addPopup=function(e){this.popups.push(e),this.updatePopups()},o.prototype.removePopup=function(e){-1!==(e=this.popups.indexOf(e))&&(this.popups.splice(e,1),this.updatePopups())},o.prototype.updatePopups=function(){this.popups.sort(function(e,t){return t.priority-e.priority});var e,t,n,i,o=[];try{for(var r=l(this.popups),a=r.next();!a.done;a=r.next()){var s=a.value,c=!0;try{n=void 0;for(var d=l(o),u=d.next();!u.done;u=d.next()){var h=u.value;if(this.doPopupsOverlap(h,s)){c=!1;break}}}catch(t){n={error:t}}finally{try{u&&!u.done&&(i=d.return)&&i.call(d)}finally{if(n)throw n.error}}c?o.push(s):s.hide()}}catch(t){e={error:t}}finally{try{a&&!a.done&&(t=r.return)&&t.call(r)}finally{if(e)throw e.error}}},o.prototype.doPopupsOverlap=function(e,t){return e=e.getElement().getBoundingClientRect(),t=t.getElement().getBoundingClientRect(),e.leftt.left&&e.topt.top};var p,f=new o,s=(t.popupManager=f,t.Tooltip=e,s(r,p=e),r.prototype.addToEditor=function(e){e.on("mousemove",this.onMouseMove),e.on("mousedown",this.hide),e.renderer.getMouseEventTarget().addEventListener("mouseout",this.onMouseOut,!0)},r.prototype.removeFromEditor=function(e){e.off("mousemove",this.onMouseMove),e.off("mousedown",this.hide),e.renderer.getMouseEventTarget().removeEventListener("mouseout",this.onMouseOut,!0),this.timeout&&(clearTimeout(this.timeout),this.timeout=null)},r.prototype.onMouseMove=function(e,t){this.lastEvent=e,this.lastT=Date.now();var n,t=t.$mouseHandler.isMousePressed;this.isOpen&&(n=this.lastEvent&&this.lastEvent.getDocumentPosition(),this.range&&this.range.contains(n.row,n.column)&&!t&&!this.isOutsideOfText(this.lastEvent)||this.hide()),this.timeout||t||(this.lastEvent=e,this.timeout=setTimeout(this.waitForHover,this.idleTime))},r.prototype.waitForHover=function(){this.timeout&&clearTimeout(this.timeout);var e=Date.now()-this.lastT;10=e.length?void 0:e)&&e[i++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},s=e("../lib/dom"),l=e("../lib/event"),c=e("../tooltip").Tooltip,d=e("../config").nls;t.GutterHandler=function(e){function t(){i=i&&clearTimeout(i),c.isOpen&&(c.hideTooltip(),r.off("mousewheel",t))}function n(e){c.setPosition(e.x,e.y)}var i,o,r=e.editor,a=r.renderer.$gutterLayer,c=new h(r);e.editor.setDefaultHandler("guttermousedown",function(t){if(r.isFocused()&&0==t.getButton()){var n=a.getRegion(t);if("foldWidgets"!=n){var n=t.getDocumentPosition().row,i=r.session.selection;if(t.getShiftKey())i.selectTo(n,0);else{if(2==t.domEvent.detail)return r.selectAll(),t.preventDefault();e.$clickSelection=r.selection.getLineRange(n)}return e.setState("selectByLines"),e.captureMouse(t),t.preventDefault()}}}),e.editor.setDefaultHandler("guttermousemove",function(l){var d=l.domEvent.target||l.domEvent.srcElement;if(s.hasCssClass(d,"ace_fold-widget"))return t();c.isOpen&&e.$tooltipFollowsMouse&&n(l),o=l,i=i||setTimeout(function(){if(i=null,o&&!e.isMousePressed){var s=o.getDocumentPosition().row;if(s==r.session.getLength()){var l=r.renderer.pixelToScreenCoordinates(0,o.y).row,d=o.$pos;if(l>r.session.documentToScreenRow(d.row,d.column))return void t()}c.showTooltip(s),c.isOpen&&(r.on("mousewheel",t),!e.$tooltipFollowsMouse&&(l=o.getGutterRow(),d=a.$lines.get(l))?(s=d.element.querySelector(".ace_gutter_annotation").getBoundingClientRect(),(l=c.getElement().style).left=s.right+"px",l.top=s.bottom+"px"):n(o))}else t()},50)}),l.addListener(r.renderer.$gutter,"mouseout",function(e){o=null,c.isOpen&&(i=i||setTimeout(function(){i=null,t()},50))},r),r.on("changeSession",t),r.on("input",t)},r(i,u=c),i.prototype.setPosition=function(e,t){var n=window.innerWidth||document.documentElement.clientWidth,i=window.innerHeight||document.documentElement.clientHeight,o=this.getWidth(),r=this.getHeight();n<(e+=15)+o&&(e-=e+o-n),i<(t+=15)+r&&(t-=20+r),c.prototype.setPosition.call(this,e,t)},Object.defineProperty(i,"annotationLabels",{get:function(){return{error:{singular:d("error"),plural:d("errors")},warning:{singular:d("warning"),plural:d("warnings")},info:{singular:d("information message"),plural:d("information messages")}}},enumerable:!1,configurable:!0}),i.prototype.showTooltip=function(e){var t=this.editor.renderer.$gutterLayer,n=t.$annotations[e],o=n?{text:Array.from(n.text),type:Array.from(n.type)}:{text:[],type:[]},r=t.session.getFoldLine(e);if(r&&t.$showFoldedAnnotations){for(var a,s={error:[],warning:[],info:[]},l=e+1;l<=r.end.row;l++)if(t.$annotations[l])for(var c=0;c ").concat(o.text[l]);u[o.type[l].replace("_fold","")].push(p)}e=[].concat(u.error,u.warning,u.info).join("
    "),this.setHtml(e),this.$element.setAttribute("aria-live","polite"),this.isOpen||(this.setTheme(this.editor.renderer.theme),this.setClassName("ace_gutter-tooltip")),this.show(),this.editor._signal("showGutterTooltip",this)},i.prototype.hideTooltip=function(){this.$element.removeAttribute("aria-live"),this.hide(),this.editor._signal("hideGutterTooltip",this)},i.annotationsToSummaryString=function(e){var t,n,o=[];try{for(var r=a(["error","warning","info"]),s=r.next();!s.done;s=r.next()){var l,c=s.value;e[c].length&&(l=1===e[c].length?i.annotationLabels[c].singular:i.annotationLabels[c].plural,o.push("".concat(e[c].length," ").concat(l)))}}catch(e){t={error:e}}finally{try{s&&!s.done&&(n=r.return)&&n.call(r)}finally{if(t)throw t.error}}return o.join(", ")};var u,h=i;t.GutterTooltip=h}),ace.define("ace/mouse/mouse_event",["require","exports","module","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";function i(e,t){this.speed,this.wheelX,this.wheelY,this.domEvent=e,this.editor=t,this.x=this.clientX=e.clientX,this.y=this.clientY=e.clientY,this.$pos=null,this.$inSelection=null,this.propagationStopped=!1,this.defaultPrevented=!1}var o=e("../lib/event"),r=e("../lib/useragent");i.prototype.stopPropagation=function(){o.stopPropagation(this.domEvent),this.propagationStopped=!0},i.prototype.preventDefault=function(){o.preventDefault(this.domEvent),this.defaultPrevented=!0},i.prototype.stop=function(){this.stopPropagation(),this.preventDefault()},i.prototype.getDocumentPosition=function(){return this.$pos||(this.$pos=this.editor.renderer.screenToTextCoordinates(this.clientX,this.clientY)),this.$pos},i.prototype.getGutterRow=function(){var e=this.getDocumentPosition().row;return this.editor.session.documentToScreenRow(e,0)-this.editor.session.documentToScreenRow(this.editor.renderer.$gutterLayer.$lines.get(0).row,0)},i.prototype.inSelection=function(){var e,t;return null===this.$inSelection&&((e=this.editor.getSelectionRange()).isEmpty()?this.$inSelection=!1:(t=this.getDocumentPosition(),this.$inSelection=e.contains(t.row,t.column))),this.$inSelection},i.prototype.getButton=function(){return o.getButton(this.domEvent)},i.prototype.getShiftKey=function(){return this.domEvent.shiftKey},i.prototype.getAccelKey=function(){return r.isMac?this.domEvent.metaKey:this.domEvent.ctrlKey},t.MouseEvent=i}),ace.define("ace/mouse/dragdrop_handler",["require","exports","module","ace/lib/dom","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";function i(e){function t(){var e,t,n,i,r,a,s,l,c=m;m=I.renderer.screenToTextCoordinates(h,p),n=m,t=c,i=Date.now(),e=!t||n.row!=t.row,t=!t||n.column!=t.column,!C||e||t?(I.moveCursorToPosition(n),C=i,x={x:h,y:p}):5this.editor.getDragDelay()&&this.startDrag()},this.dragWaitEnd=function(){this.editor.container.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()),this.selectEnd()},this.dragReadyEnd=function(e){this.editor.$resetCursorStyle(),this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle(""),this.dragWaitEnd()},this.startDrag=function(){this.cancelDrag=!1;var e=this.editor,t=(e.container.draggable=!0,e.renderer.$cursorLayer.setBlinking(!1),e.setStyle("ace_dragging"),s.isWin?"default":"move");e.renderer.setCursorStyle(t),this.setState("dragReady")},this.onMouseDrag=function(e){var t=this.editor.container;s.isIE&&"dragReady"==this.state&&3o&&(u=-1),c=e.clientX=f,d=e.clientY=s,x=I=0,new i(e,t));if(g=f.getDocumentPosition(),a-u<500&&1==n.length&&!b)C++,e.preventDefault(),e.button=0,p=null,clearTimeout(p),t.selection.moveToPosition(g),(s=2<=C?t.selection.getLineRange(g.row):t.session.getBracketRange(g))&&!s.isEmpty()?t.selection.setRange(s):t.selection.selectWord(),v="wait";else{C=0;var f=t.selection.cursor,n=t.selection.isEmpty()?f:t.selection.anchor,s=t.renderer.$cursorLayer.getPixelPosition(f,!0),f=t.renderer.$cursorLayer.getPixelPosition(n,!0),n=t.renderer.scroller.getBoundingClientRect(),y=t.renderer.layerConfig.offset,w=t.renderer.scrollLeft,A=function(e,t){return(e/=r)*e+(t=t/o-.75)*t};if(e.clientX=t.length||(o=n[i-1])!=g&&o!=m||(h=t[i+1])!=g&&h!=m?y:(h=l?m:h)==o?h:y;case w:return(o=0=e){for(i=u+1;i=e;)i++;for(o=u,r=i-1;o>8;return 0==n?191C&&n[u]<_||n[u]===y||n[u]===$)?d[u]=t.ON_R:0=e.left&&t.right+n<=e.right&&t.top-n>=e.top&&t.bottom+n<=e.bottom}function W(e,t,n){var i;return function(){var o=this,r=arguments,a=n&&!i;clearTimeout(i),i=setTimeout(function(){i=null,n||e.apply(o,r)},t),a&&e.apply(o,r)}}function V(e,t){for(var n=t.length,i=0,o=e.length,r=t.length;t.charAt(i)===e.charAt(i)&&ithis.length)&&(t=this.length),t-=e.length,-1!==(e=this.indexOf(e,t))&&e===t}),String.prototype.repeat||i(String.prototype,"repeat",function(e){for(var t="",n=this;0>=1)&&(n+=n);return t}),String.prototype.includes||i(String.prototype,"includes",function(e,t){return-1!=this.indexOf(e,t)}),Object.assign||(Object.assign=function(e){if(null==e)throw new TypeError("Cannot convert undefined or null to object");for(var t=Object(e),n=1;n>>0,n=arguments[1]>>0,i=n<0?Math.max(t+n,0):Math.min(n,t),n=arguments[2],n=void 0===n?t:n>>0,o=n<0?Math.max(t+n,0):Math.min(n,t);i>=1)&&(e+=e);return n};var i=/^\s\s*/,o=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(i,"")},t.stringTrimRight=function(e){return e.replace(o,"")},t.copyObject=function(e){var t,n={};for(t in e)n[t]=e[t];return n},t.copyArray=function(e){for(var t=[],n=0,i=e.length;n ["+this.end.row+"/"+this.end.column+"]"},i.prototype.contains=function(e,t){return 0==this.compare(e,t)},i.prototype.compareRange=function(e){var t=e.end,e=e.start,t=this.compare(t.row,t.column);return 1==t?1==(t=this.compare(e.row,e.column))?2:0==t?1:0:-1==t?-2:-1==(t=this.compare(e.row,e.column))?-1:1==t?42:0},i.prototype.comparePoint=function(e){return this.compare(e.row,e.column)},i.prototype.containsRange=function(e){return 0==this.comparePoint(e.start)&&0==this.comparePoint(e.end)},i.prototype.intersects=function(e){return-1==(e=this.compareRange(e))||0==e||1==e},i.prototype.isEnd=function(e,t){return this.end.row==e&&this.end.column==t},i.prototype.isStart=function(e,t){return this.start.row==e&&this.start.column==t},i.prototype.setStart=function(e,t){"object"==(void 0===e?"undefined":_typeof(e))?(this.start.column=e.column,this.start.row=e.row):(this.start.row=e,this.start.column=t)},i.prototype.setEnd=function(e,t){"object"==(void 0===e?"undefined":_typeof(e))?(this.end.column=e.column,this.end.row=e.row):(this.end.row=e,this.end.column=t)},i.prototype.inside=function(e,t){return 0==this.compare(e,t)&&!this.isEnd(e,t)&&!this.isStart(e,t)},i.prototype.insideStart=function(e,t){return 0==this.compare(e,t)&&!this.isEnd(e,t)},i.prototype.insideEnd=function(e,t){return 0==this.compare(e,t)&&!this.isStart(e,t)},i.prototype.compare=function(e,t){return this.isMultiLine()||e!==this.start.row?ethis.end.row?1:this.start.row===e?t>=this.start.column?0:-1:this.end.row!==e||t<=this.end.column?0:1:tthis.end.column?1:0},i.prototype.compareStart=function(e,t){return this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},i.prototype.compareEnd=function(e,t){return this.end.row==e&&this.end.column==t?1:this.compare(e,t)},i.prototype.compareInside=function(e,t){return this.end.row==e&&this.end.column==t?1:this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},i.prototype.clipRows=function(e,t){var n,o;return this.end.row>t?n={row:t+1,column:0}:this.end.rowt?o={row:t+1,column:0}:this.start.rowDate.now()-50)||(i=!1)},cancel:function(){i=Date.now()}}}),ace.define("ace/keyboard/textinput",["require","exports","module","ace/lib/event","ace/config","ace/lib/useragent","ace/lib/dom","ace/lib/lang","ace/clipboard","ace/lib/keys"],function(e,t,n){"use strict";var i=e("../lib/event"),o=e("../config").nls,r=e("../lib/useragent"),a=e("../lib/dom"),s=e("../lib/lang"),l=e("../clipboard"),c=r.isChrome<18,d=r.isIE,u=63M?(r=t.session.getLine(M+1),i=s.end.row>M+1?r.length:s.end.column,i+=o.length+1,o=o+"\n"+r):m&&0=$.length&&e.value===$&&$&&e.selectionEnd!==O}),W=null,V=(this.setInputHandler=function(e){W=e},!(this.getInputHandler=function(){return W})),G=function(e,n){if(V=V&&!1,k)return P(),e&&t.onPaste(e),k=!1,"";for(var i=w.selectionStart,o=w.selectionEnd,a=L,s=$.length-O,l=e,c=e.length-i,d=e.length-o,u=0;0L-1&&$[$.length-u]==e[e.length-u];)u++,s--;c-=u-1,d-=u-1;var h=l.length-u+1;return h<0&&(a=-h,h=0),l=l.slice(0,h),n||l||c||a||s||d?(h=!(S=!0),r.isAndroid&&". "==l&&(l=" ",h=!0),l&&!a&&!s&&!c&&!d||E?t.onTextInput(l):t.onTextInput(l,{extendLeft:a,extendRight:s,restoreStart:c,restoreEnd:d}),S=!1,$=e,L=i,O=o,j=d,h?"\n":l):""},Z=function(e){if(_)return X();if(e&&e.inputType){if("historyUndo"==e.inputType)return t.execCommand("undo");if("historyRedo"==e.inputType)return t.execCommand("redo")}var e=w.value,n=G(e,!0);(500this.$focusTimeout)&&this.startSelect(this.mousedownEvent.getDocumentPosition())},i.prototype.onDoubleClick=function(e){var e=e.getDocumentPosition(),t=this.editor,n=t.session.getBracketRange(e);n?(n.isEmpty()&&(n.start.column--,n.end.column++),this.setState("select")):(n=t.selection.getWordRange(e.row,e.column),this.setState("selectByWords")),this.$clickSelection=n,this.select()},i.prototype.onTripleClick=function(e){var e=e.getDocumentPosition(),t=this.editor,n=(this.setState("selectByLines"),t.getSelectionRange());n.isMultiLine()&&n.contains(e.row,e.column)?(this.$clickSelection=t.selection.getLineRange(n.start.row),this.$clickSelection.end=t.selection.getLineRange(n.end.row).end):this.$clickSelection=t.selection.getLineRange(e.row),this.select()},i.prototype.onQuadClick=function(e){var t=this.editor;t.selectAll(),this.$clickSelection=t.getSelectionRange(),this.setState("selectAll")},i.prototype.onMouseWheel=function(e){var t,n,i,o,r,a,s;if(!e.getAccelKey())return e.getShiftKey()&&e.wheelY&&!e.wheelX&&(e.wheelX=e.wheelY,e.wheelY=0),t=this.editor,this.$lastScroll||(this.$lastScroll={t:0,vx:0,vy:0,allowed:0}),n=this.$lastScroll,o=(s=(i=e.domEvent.timeStamp)-n.t)?e.wheelX/s:n.vx,r=s?e.wheelY/s:n.vy,s<550&&(o=(o+n.vx)/2,r=(r+n.vy)/2),s=!1,1<=(a=Math.abs(o/r))&&t.renderer.isScrollableBy(e.wheelX*e.speed,0)&&(s=!0),(s=!!(a<=1&&t.renderer.isScrollableBy(0,e.wheelY*e.speed))||s)?n.allowed=i:i-n.allowed<550&&(Math.abs(o)<=1.5*Math.abs(n.vx)&&Math.abs(r)<=1.5*Math.abs(n.vy)?(s=!0,n.allowed=i):n.allowed=0),n.t=i,n.vx=o,n.vy=r,s?(t.renderer.scrollBy(e.wheelX*e.speed,e.wheelY*e.speed),e.stop()):void 0},i);e.prototype.selectEnd=e.prototype.selectByLinesEnd,e.prototype.selectAllEnd=e.prototype.selectByLinesEnd,e.prototype.selectByWordsEnd=e.prototype.selectByLinesEnd,t.DefaultHandlers=e}),ace.define("ace/lib/scroll",["require","exports","module"],function(e,t,n){t.preventParentScroll=function(e){e.stopPropagation();var t=e.currentTarget;t.scrollHeight>t.clientHeight||e.preventDefault()}}),ace.define("ace/tooltip",["require","exports","module","ace/lib/dom","ace/lib/event","ace/range","ace/lib/scroll"],function(e,t,n){"use strict";function i(e){this.isOpen=!1,this.$element=null,this.$parentNode=e}function o(){this.popups=[]}function r(e){void 0===e&&(e=document.body);var e=p.call(this,e)||this,t=(e.timeout=void 0,e.lastT=0,e.idleTime=350,e.lastEvent=void 0,e.onMouseOut=e.onMouseOut.bind(e),e.onMouseMove=e.onMouseMove.bind(e),e.waitForHover=e.waitForHover.bind(e),e.hide=e.hide.bind(e),e.getElement());return t.style.whiteSpace="pre-wrap",t.style.pointerEvents="auto",t.addEventListener("mouseout",e.onMouseOut),t.tabIndex=-1,t.addEventListener("blur",function(){t.contains(document.activeElement)||this.hide()}.bind(e)),t.addEventListener("wheel",u),e}var a,s=this&&this.__extends||(a=function(e,t){return(a=Object.setPrototypeOf||({__proto__:[]}instanceof Array?function(e,t){e.__proto__=t}:function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])}))(e,t)},function(e,t){function n(){this.constructor=e}if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");a(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),l=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,n=t&&e[t],i=0;if(n)return n.call(e);if(e&&"number"==typeof e.length)return{next:function(){return{value:(e=e&&i>=e.length?void 0:e)&&e[i++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},c=e("./lib/dom"),d=(e("./lib/event"),e("./range").Range),u=e("./lib/scroll").preventParentScroll,h="ace_tooltip",e=(i.prototype.$init=function(){return this.$element=c.createElement("div"),this.$element.className=h,this.$element.style.display="none",this.$parentNode.appendChild(this.$element),this.$element},i.prototype.getElement=function(){return this.$element||this.$init()},i.prototype.setText=function(e){this.getElement().textContent=e},i.prototype.setHtml=function(e){this.getElement().innerHTML=e},i.prototype.setPosition=function(e,t){this.getElement().style.left=e+"px",this.getElement().style.top=t+"px"},i.prototype.setClassName=function(e){c.addCssClass(this.getElement(),e)},i.prototype.setTheme=function(e){this.$element.className=h+" "+(e.isDark?"ace_dark ":"")+(e.cssClass||"")},i.prototype.show=function(e,t,n){null!=e&&this.setText(e),null!=t&&null!=n&&this.setPosition(t,n),this.isOpen||(this.getElement().style.display="block",this.isOpen=!0)},i.prototype.hide=function(e){this.isOpen&&(this.getElement().style.display="none",this.getElement().className=h,this.isOpen=!1)},i.prototype.getHeight=function(){return this.getElement().offsetHeight},i.prototype.getWidth=function(){return this.getElement().offsetWidth},i.prototype.destroy=function(){this.isOpen=!1,this.$element&&this.$element.parentNode&&this.$element.parentNode.removeChild(this.$element)},i);o.prototype.addPopup=function(e){this.popups.push(e),this.updatePopups()},o.prototype.removePopup=function(e){-1!==(e=this.popups.indexOf(e))&&(this.popups.splice(e,1),this.updatePopups())},o.prototype.updatePopups=function(){this.popups.sort(function(e,t){return t.priority-e.priority});var e,t,n,i,o=[];try{for(var r=l(this.popups),a=r.next();!a.done;a=r.next()){var s=a.value,c=!0;try{n=void 0;for(var d=l(o),u=d.next();!u.done;u=d.next()){var h=u.value;if(this.doPopupsOverlap(h,s)){c=!1;break}}}catch(t){n={error:t}}finally{try{u&&!u.done&&(i=d.return)&&i.call(d)}finally{if(n)throw n.error}}c?o.push(s):s.hide()}}catch(t){e={error:t}}finally{try{a&&!a.done&&(t=r.return)&&t.call(r)}finally{if(e)throw e.error}}},o.prototype.doPopupsOverlap=function(e,t){return e=e.getElement().getBoundingClientRect(),t=t.getElement().getBoundingClientRect(),e.leftt.left&&e.topt.top};var p,f=new o,s=(t.popupManager=f,t.Tooltip=e,s(r,p=e),r.prototype.addToEditor=function(e){e.on("mousemove",this.onMouseMove),e.on("mousedown",this.hide),e.renderer.getMouseEventTarget().addEventListener("mouseout",this.onMouseOut,!0)},r.prototype.removeFromEditor=function(e){e.off("mousemove",this.onMouseMove),e.off("mousedown",this.hide),e.renderer.getMouseEventTarget().removeEventListener("mouseout",this.onMouseOut,!0),this.timeout&&(clearTimeout(this.timeout),this.timeout=null)},r.prototype.onMouseMove=function(e,t){this.lastEvent=e,this.lastT=Date.now();var n,t=t.$mouseHandler.isMousePressed;this.isOpen&&(n=this.lastEvent&&this.lastEvent.getDocumentPosition(),this.range&&this.range.contains(n.row,n.column)&&!t&&!this.isOutsideOfText(this.lastEvent)||this.hide()),this.timeout||t||(this.lastEvent=e,this.timeout=setTimeout(this.waitForHover,this.idleTime))},r.prototype.waitForHover=function(){this.timeout&&clearTimeout(this.timeout);var e=Date.now()-this.lastT;10=e.length?void 0:e)&&e[i++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},s=e("../lib/dom"),l=e("../lib/event"),c=e("../tooltip").Tooltip,d=e("../config").nls;t.GutterHandler=function(e){function t(){i=i&&clearTimeout(i),c.isOpen&&(c.hideTooltip(),r.off("mousewheel",t))}function n(e){c.setPosition(e.x,e.y)}var i,o,r=e.editor,a=r.renderer.$gutterLayer,c=new h(r);e.editor.setDefaultHandler("guttermousedown",function(t){if(r.isFocused()&&0==t.getButton()){var n=a.getRegion(t);if("foldWidgets"!=n){var n=t.getDocumentPosition().row,i=r.session.selection;if(t.getShiftKey())i.selectTo(n,0);else{if(2==t.domEvent.detail)return r.selectAll(),t.preventDefault();e.$clickSelection=r.selection.getLineRange(n)}return e.setState("selectByLines"),e.captureMouse(t),t.preventDefault()}}}),e.editor.setDefaultHandler("guttermousemove",function(l){var d=l.domEvent.target||l.domEvent.srcElement;if(s.hasCssClass(d,"ace_fold-widget"))return t();c.isOpen&&e.$tooltipFollowsMouse&&n(l),o=l,i=i||setTimeout(function(){if(i=null,o&&!e.isMousePressed){var s=o.getDocumentPosition().row;if(s==r.session.getLength()){var l=r.renderer.pixelToScreenCoordinates(0,o.y).row,d=o.$pos;if(l>r.session.documentToScreenRow(d.row,d.column))return void t()}c.showTooltip(s),c.isOpen&&(r.on("mousewheel",t),!e.$tooltipFollowsMouse&&(l=o.getGutterRow(),d=a.$lines.get(l))?(s=d.element.querySelector(".ace_gutter_annotation").getBoundingClientRect(),(l=c.getElement().style).left=s.right+"px",l.top=s.bottom+"px"):n(o))}else t()},50)}),l.addListener(r.renderer.$gutter,"mouseout",function(e){o=null,c.isOpen&&(i=i||setTimeout(function(){i=null,t()},50))},r),r.on("changeSession",t),r.on("input",t)},r(i,u=c),i.prototype.setPosition=function(e,t){var n=window.innerWidth||document.documentElement.clientWidth,i=window.innerHeight||document.documentElement.clientHeight,o=this.getWidth(),r=this.getHeight();n<(e+=15)+o&&(e-=e+o-n),i<(t+=15)+r&&(t-=20+r),c.prototype.setPosition.call(this,e,t)},Object.defineProperty(i,"annotationLabels",{get:function(){return{error:{singular:d("error"),plural:d("errors")},warning:{singular:d("warning"),plural:d("warnings")},info:{singular:d("information message"),plural:d("information messages")}}},enumerable:!1,configurable:!0}),i.prototype.showTooltip=function(e){var t=this.editor.renderer.$gutterLayer,n=t.$annotations[e],o=n?{text:Array.from(n.text),type:Array.from(n.type)}:{text:[],type:[]},r=t.session.getFoldLine(e);if(r&&t.$showFoldedAnnotations){for(var a,s={error:[],warning:[],info:[]},l=e+1;l<=r.end.row;l++)if(t.$annotations[l])for(var c=0;c ").concat(o.text[l]);u[o.type[l].replace("_fold","")].push(p)}e=[].concat(u.error,u.warning,u.info).join("
    "),this.setHtml(e),this.$element.setAttribute("aria-live","polite"),this.isOpen||(this.setTheme(this.editor.renderer.theme),this.setClassName("ace_gutter-tooltip")),this.show(),this.editor._signal("showGutterTooltip",this)},i.prototype.hideTooltip=function(){this.$element.removeAttribute("aria-live"),this.hide(),this.editor._signal("hideGutterTooltip",this)},i.annotationsToSummaryString=function(e){var t,n,o=[];try{for(var r=a(["error","warning","info"]),s=r.next();!s.done;s=r.next()){var l,c=s.value;e[c].length&&(l=1===e[c].length?i.annotationLabels[c].singular:i.annotationLabels[c].plural,o.push("".concat(e[c].length," ").concat(l)))}}catch(e){t={error:e}}finally{try{s&&!s.done&&(n=r.return)&&n.call(r)}finally{if(t)throw t.error}}return o.join(", ")};var u,h=i;t.GutterTooltip=h}),ace.define("ace/mouse/mouse_event",["require","exports","module","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";function i(e,t){this.speed,this.wheelX,this.wheelY,this.domEvent=e,this.editor=t,this.x=this.clientX=e.clientX,this.y=this.clientY=e.clientY,this.$pos=null,this.$inSelection=null,this.propagationStopped=!1,this.defaultPrevented=!1}var o=e("../lib/event"),r=e("../lib/useragent");i.prototype.stopPropagation=function(){o.stopPropagation(this.domEvent),this.propagationStopped=!0},i.prototype.preventDefault=function(){o.preventDefault(this.domEvent),this.defaultPrevented=!0},i.prototype.stop=function(){this.stopPropagation(),this.preventDefault()},i.prototype.getDocumentPosition=function(){return this.$pos||(this.$pos=this.editor.renderer.screenToTextCoordinates(this.clientX,this.clientY)),this.$pos},i.prototype.getGutterRow=function(){var e=this.getDocumentPosition().row;return this.editor.session.documentToScreenRow(e,0)-this.editor.session.documentToScreenRow(this.editor.renderer.$gutterLayer.$lines.get(0).row,0)},i.prototype.inSelection=function(){var e,t;return null===this.$inSelection&&((e=this.editor.getSelectionRange()).isEmpty()?this.$inSelection=!1:(t=this.getDocumentPosition(),this.$inSelection=e.contains(t.row,t.column))),this.$inSelection},i.prototype.getButton=function(){return o.getButton(this.domEvent)},i.prototype.getShiftKey=function(){return this.domEvent.shiftKey},i.prototype.getAccelKey=function(){return r.isMac?this.domEvent.metaKey:this.domEvent.ctrlKey},t.MouseEvent=i}),ace.define("ace/mouse/dragdrop_handler",["require","exports","module","ace/lib/dom","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";function i(e){function t(){var e,t,n,i,r,a,s,l,c=m;m=I.renderer.screenToTextCoordinates(h,p),n=m,t=c,i=Date.now(),e=!t||n.row!=t.row,t=!t||n.column!=t.column,!C||e||t?(I.moveCursorToPosition(n),C=i,x={x:h,y:p}):5this.editor.getDragDelay()&&this.startDrag()},this.dragWaitEnd=function(){this.editor.container.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()),this.selectEnd()},this.dragReadyEnd=function(e){this.editor.$resetCursorStyle(),this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle(""),this.dragWaitEnd()},this.startDrag=function(){this.cancelDrag=!1;var e=this.editor,t=(e.container.draggable=!0,e.renderer.$cursorLayer.setBlinking(!1),e.setStyle("ace_dragging"),s.isWin?"default":"move");e.renderer.setCursorStyle(t),this.setState("dragReady")},this.onMouseDrag=function(e){var t=this.editor.container;s.isIE&&"dragReady"==this.state&&3o&&(u=-1),c=e.clientX=f,d=e.clientY=s,x=I=0,new i(e,t));if(g=f.getDocumentPosition(),a-u<500&&1==n.length&&!b)C++,e.preventDefault(),e.button=0,p=null,clearTimeout(p),t.selection.moveToPosition(g),(s=2<=C?t.selection.getLineRange(g.row):t.session.getBracketRange(g))&&!s.isEmpty()?t.selection.setRange(s):t.selection.selectWord(),v="wait";else{C=0;var f=t.selection.cursor,n=t.selection.isEmpty()?f:t.selection.anchor,s=t.renderer.$cursorLayer.getPixelPosition(f,!0),f=t.renderer.$cursorLayer.getPixelPosition(n,!0),n=t.renderer.scroller.getBoundingClientRect(),y=t.renderer.layerConfig.offset,w=t.renderer.scrollLeft,A=function(e,t){return(e/=r)*e+(t=t/o-.75)*t};if(e.clientX=t.length||(o=n[i-1])!=g&&o!=m||(h=t[i+1])!=g&&h!=m?y:(h=l?m:h)==o?h:y;case w:return(o=0=e){for(i=u+1;i=e;)i++;for(o=u,r=i-1;o>8;return 0==n?191C&&n[u]<_||n[u]===y||n[u]===$)?d[u]=t.ON_R:0t.row||e.row==t.row&&e.column>t.column},i.prototype.getRange=function(){var e=this.anchor,t=this.lead;return this.$isEmpty?s.fromPoints(t,t):this.isBackwards()?s.fromPoints(t,e):s.fromPoints(e,t)},i.prototype.clearSelection=function(){this.$isEmpty||(this.$isEmpty=!0,this._emit("changeSelection"))},i.prototype.selectAll=function(){this.$setSelection(0,0,Number.MAX_VALUE,Number.MAX_VALUE)},i.prototype.setRange=function(e,t){var n=t?e.end:e.start,t=t?e.start:e.end;this.$setSelection(n.row,n.column,t.row,t.column)},i.prototype.$setSelection=function(e,t,n,i){var o,r;!this.$silent&&(o=this.$isEmpty,r=this.inMultiSelectMode,this.$silent=!0,this.$cursorChanged=this.$anchorChanged=!1,this.anchor.setPosition(e,t),this.cursor.setPosition(n,i),this.$isEmpty=!s.comparePoints(this.anchor,this.cursor),this.$silent=!1,this.$cursorChanged&&this._emit("changeCursor"),this.$cursorChanged||this.$anchorChanged||o!=this.$isEmpty||r)&&this._emit("changeSelection")},i.prototype.$moveSelection=function(e){var t=this.lead;this.$isEmpty&&this.setSelectionAnchor(t.row,t.column),e.call(this)},i.prototype.selectTo=function(e,t){this.$moveSelection(function(){this.moveCursorTo(e,t)})},i.prototype.selectToPosition=function(e){this.$moveSelection(function(){this.moveCursorToPosition(e)})},i.prototype.moveTo=function(e,t){this.clearSelection(),this.moveCursorTo(e,t)},i.prototype.moveToPosition=function(e){this.clearSelection(),this.moveCursorToPosition(e)},i.prototype.selectUp=function(){this.$moveSelection(this.moveCursorUp)},i.prototype.selectDown=function(){this.$moveSelection(this.moveCursorDown)},i.prototype.selectRight=function(){this.$moveSelection(this.moveCursorRight)},i.prototype.selectLeft=function(){this.$moveSelection(this.moveCursorLeft)},i.prototype.selectLineStart=function(){this.$moveSelection(this.moveCursorLineStart)},i.prototype.selectLineEnd=function(){this.$moveSelection(this.moveCursorLineEnd)},i.prototype.selectFileEnd=function(){this.$moveSelection(this.moveCursorFileEnd)},i.prototype.selectFileStart=function(){this.$moveSelection(this.moveCursorFileStart)},i.prototype.selectWordRight=function(){this.$moveSelection(this.moveCursorWordRight)},i.prototype.selectWordLeft=function(){this.$moveSelection(this.moveCursorWordLeft)},i.prototype.getWordRange=function(e,t){var n;return void 0===t&&(e=(n=e||this.lead).row,t=n.column),this.session.getWordRange(e,t)},i.prototype.selectWord=function(){this.setSelectionRange(this.getWordRange())},i.prototype.selectAWord=function(){var e=this.getCursor(),e=this.session.getAWordRange(e.row,e.column);this.setSelectionRange(e)},i.prototype.getLineRange=function(e,t){var e="number"==typeof e?e:this.lead.row,n=this.session.getFoldLine(e),n=n?(e=n.start.row,n.end.row):e;return!0===t?new s(e,0,n,this.session.getLine(n).length):new s(e,0,n+1,0)},i.prototype.selectLine=function(){this.setSelectionRange(this.getLineRange())},i.prototype.moveCursorUp=function(){this.moveCursorBy(-1,0)},i.prototype.moveCursorDown=function(){this.moveCursorBy(1,0)},i.prototype.wouldMoveIntoSoftTab=function(e,t,n){var i=e.column,o=e.column+t;return n<0&&(i=e.column-t,o=e.column),this.session.isTabStop(e)&&this.doc.getLine(e.row).slice(i,o).split(" ").length-1==t},i.prototype.moveCursorLeft=function(){var e,t=this.lead.getPosition();(e=this.session.getFoldAt(t.row,t.column,-1))?this.moveCursorTo(e.start.row,e.start.column):0===t.column?0=n.length?(this.moveCursorTo(e,n.length),this.moveCursorRight(),ed&&(p=e.substring(d,y-m.length),h.type==f?h.value+=p:(h.type&&c.push(h),h={type:f,value:p}));for(var v=0;vo){for(u>2*e.length&&this.reportError("infinite loop with in ace tokenizer",{startState:t,line:e});d=this.$rowTokens.length;){if(this.$row+=1,e=e||this.$session.getLength(),this.$row>=e)return this.$row=e-1,null;this.$rowTokens=this.$session.getTokens(this.$row),this.$tokenIndex=0}return this.$rowTokens[this.$tokenIndex]},i.prototype.getCurrentToken=function(){return this.$rowTokens[this.$tokenIndex]},i.prototype.getCurrentTokenRow=function(){return this.$row},i.prototype.getCurrentTokenColumn=function(){var e=this.$rowTokens,t=this.$tokenIndex,n=e[t].start;if(void 0===n)for(n=0;0e.length&&(x=e.length)}),c==1/0&&(c=x,s=a=!1),u&&c%d!=0&&(c=Math.floor(c/d)*d),o(s?h:f)},this.toggleBlockComment=function(e,t,n,i){var o=this.blockComment;if(o){!o.start&&o[0]&&(o=o[0]);var r,a,s=(f=new c(t,i.row,i.column)).getCurrentToken(),l=(t.selection,t.selection.toOrientedRange());if(s&&/comment/.test(s.type)){for(;s&&/comment/.test(s.type);){if(-1!=(g=s.value.indexOf(o.start))){var u=f.getCurrentTokenRow(),h=f.getCurrentTokenColumn()+g,p=new d(u,h,u,h+o.start.length);break}s=f.stepBackward()}for(var f,g,s=(f=new c(t,i.row,i.column)).getCurrentToken();s&&/comment/.test(s.type);){if(-1!=(g=s.value.indexOf(o.end))){var u=f.getCurrentTokenRow(),h=f.getCurrentTokenColumn()+g,m=new d(u,h,u,h+o.end.length);break}s=f.stepForward()}m&&t.remove(m),p&&(t.remove(p),r=p.start.row,a=-o.start.length)}else a=o.start.length,r=n.start.row,t.insert(n.end,o.end),t.insert(n.start,o.start);l.start.row==r&&(l.start.column+=a),l.end.row==r&&(l.end.column+=a),t.selection.fromOrientedRange(l)}},this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.autoOutdent=function(e,t,n){},this.$getIndent=function(e){return e.match(/^\s*/)[0]},this.createWorker=function(e){return null},this.createModeDelegates=function(e){for(var t in this.$embeds=[],this.$modes={},e){var n,o,r;e[t]&&(o=(n=e[t]).prototype.$id,(r=i.$modes[o])||(i.$modes[o]=r=new n),i.$modes[t]||(i.$modes[t]=r),this.$embeds.push(t),this.$modes[t]=r)}for(var a=["toggleBlockComment","toggleCommentLines","getNextLineIndent","checkOutdent","autoOutdent","transformAction","getCompletions"],s=this,t=0;tthis.row||(e=function(e,t,n){var i="insert"==e.action,r=(i?1:-1)*(e.end.row-e.start.row),a=(i?1:-1)*(e.end.column-e.start.column),s=e.start,i=i?s:e.end;return o(t,s,n)?{row:t.row,column:t.column}:o(i,t,!n)?{row:t.row+r,column:t.column+(t.row==i.row?a:0)}:{row:s.row,column:s.column}}(e,{row:this.row,column:this.column},this.$insertRight),this.setPosition(e.row,e.column,!0))},i.prototype.setPosition=function(e,t,n){n=n?{row:e,column:t}:this.$clipPositionToDocument(e,t),this.row==n.row&&this.column==n.column||(e={row:this.row,column:this.column},this.row=n.row,this.column=n.column,this._signal("change",{old:e,value:n}))},i.prototype.detach=function(){this.document.off("change",this.$onChange)},i.prototype.attach=function(e){this.document=e||this.document,this.document.on("change",this.$onChange)},i.prototype.$clipPositionToDocument=function(e,t){var n={};return e>=this.document.getLength()?(n.row=Math.max(0,this.document.getLength()-1),n.column=this.document.getLine(n.row).length):e<0?(n.row=0,n.column=0):(n.row=e,n.column=Math.min(this.document.getLine(n.row).length,Math.max(0,t))),t<0&&(n.column=0),n},i);a.prototype.$insertRight=!1,r.implement(a.prototype,e),t.Anchor=a}),ace.define("ace/document",["require","exports","module","ace/lib/oop","ace/apply_delta","ace/lib/event_emitter","ace/range","ace/anchor"],function(e,t,n){"use strict";function i(e){this.$lines=[""],0===e.length?this.$lines=[""]:Array.isArray(e)?this.insertMergedLines({row:0,column:0},e):this.insert({row:0,column:0},e)}var o=e("./lib/oop"),r=e("./apply_delta").applyDelta,a=e("./lib/event_emitter").EventEmitter,s=e("./range").Range,l=e("./anchor").Anchor,e=(i.prototype.setValue=function(e){var t=this.getLength()-1;this.remove(new s(0,0,t,this.getLine(t).length)),this.insert({row:0,column:0},e||"")},i.prototype.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},i.prototype.createAnchor=function(e,t){return new l(this,e,t)},i.prototype.$detectNewLine=function(e){e=e.match(/^.*?(\r\n|\r|\n)/m),this.$autoNewLine=e?e[1]:"\n",this._signal("changeNewLineMode")},i.prototype.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";default:return this.$autoNewLine||"\n"}},i.prototype.setNewLineMode=function(e){this.$newLineMode!==e&&(this.$newLineMode=e,this._signal("changeNewLineMode"))},i.prototype.getNewLineMode=function(){return this.$newLineMode},i.prototype.isNewLine=function(e){return"\r\n"==e||"\r"==e||"\n"==e},i.prototype.getLine=function(e){return this.$lines[e]||""},i.prototype.getLines=function(e,t){return this.$lines.slice(e,t+1)},i.prototype.getAllLines=function(){return this.getLines(0,this.getLength())},i.prototype.getLength=function(){return this.$lines.length},i.prototype.getTextRange=function(e){return this.getLinesForRange(e).join(this.getNewLineCharacter())},i.prototype.getLinesForRange=function(e){var t,n;return e.start.row===e.end.row?t=[this.getLine(e.start.row).substring(e.start.column,e.end.column)]:((t=this.getLines(e.start.row,e.end.row))[0]=(t[0]||"").substring(e.start.column),n=t.length-1,e.end.row-e.start.row==n&&(t[n]=t[n].substring(0,e.end.column))),t},i.prototype.insertLines=function(e,t){return console.warn("Use of document.insertLines is deprecated. Use the insertFullLines method instead."),this.insertFullLines(e,t)},i.prototype.removeLines=function(e,t){return console.warn("Use of document.removeLines is deprecated. Use the removeFullLines method instead."),this.removeFullLines(e,t)},i.prototype.insertNewLine=function(e){return console.warn("Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead."),this.insertMergedLines(e,["",""])},i.prototype.insert=function(e,t){return this.getLength()<=1&&this.$detectNewLine(t),this.insertMergedLines(e,this.$split(t))},i.prototype.insertInLine=function(e,t){var n=this.clippedPos(e.row,e.column),e=this.pos(e.row,e.column+t.length);return this.applyDelta({start:n,end:e,action:"insert",lines:[t]},!0),this.clonePos(e)},i.prototype.clippedPos=function(e,t){var n=this.getLength(),n=(void 0===e?e=n:e<0?e=0:n<=e&&(e=n-1,t=void 0),this.getLine(e));return null==t&&(t=n.length),{row:e,column:t=Math.min(Math.max(t,0),n.length)}},i.prototype.clonePos=function(e){return{row:e.row,column:e.column}},i.prototype.pos=function(e,t){return{row:e,column:t}},i.prototype.$clipPosition=function(e){var t=this.getLength();return e.row>=t?(e.row=Math.max(0,t-1),e.column=this.getLine(t-1).length):(e.row=Math.max(0,e.row),e.column=Math.min(Math.max(e.column,0),this.getLine(e.row).length)),e},i.prototype.insertFullLines=function(e,t){var n=0,n=(e=Math.min(Math.max(e,0),this.getLength()))e+1&&(this.currentLine=e+1)):this.currentLine==e&&(this.currentLine=e+1),this.lines[e]=t.tokens},i.prototype.cleanup=function(){this.running=!1,this.lines=[],this.states=[],this.currentLine=0,this.removeAllListeners()},i);o.implement(r.prototype,e),t.BackgroundTokenizer=r}),ace.define("ace/search_highlight",["require","exports","module","ace/lib/lang","ace/range"],function(e,t,n){"use strict";function i(e,t,n){void 0===n&&(n="text"),this.setRegexp(e),this.clazz=t,this.type=n}var o=e("./lib/lang"),r=e("./range").Range,e=(i.prototype.setRegexp=function(e){this.regExp+""!=e+""&&(this.regExp=e,this.cache=[])},i.prototype.update=function(e,t,n,i){if(this.regExp)for(var a=i.firstRow,s=i.lastRow,l={},c=a;c<=s;c++){var d=this.cache[c];null==d&&(d=(d=(d=o.getMatchOffsets(n.getLine(c),this.regExp)).length>this.MAX_RANGES?d.slice(0,this.MAX_RANGES):d).map(function(e){return new r(c,e.offset,c,e.offset+e.length)}),this.cache[c]=d.length?d:"");for(var u=d.length;u--;){var h=d[u].toScreenRange(n),p=h.toString();l[p]||(l[p]=!0,t.drawSingleLineMarker(e,h,this.clazz,i))}}},i);e.prototype.MAX_RANGES=500,t.SearchHighlight=e}),ace.define("ace/undomanager",["require","exports","module","ace/range"],function(e,t,n){"use strict";function i(){this.$keepRedoStack,this.$maxRev=0,this.$fromUndo=!1,this.$undoDepth=1/0,this.reset()}function o(e){return{row:e.row,column:e.column}}function r(e){var t;return e=e||this,Array.isArray(e)?e.map(r).join("\n"):(t="",e.action?(t="insert"==e.action?"+":"-",t+="["+e.lines+"]"):e.value&&(t=Array.isArray(e.value)?e.value.map(a).join("\n"):a(e.value)),e.start&&(t+=a(e)),(e.id||e.rev)&&(t+="\t("+(e.id||e.rev)+")"),t)}function a(e){return e.start.row+":"+e.start.column+"=>"+e.end.row+":"+e.end.column}function s(e,t){var n="insert"==e.action,i="insert"==t.action;if(n&&i)if(0<=p(t.start,e.end))l(t,e,-1);else{if(!(p(t.start,e.start)<=0))return;l(e,t,1)}else if(n&&!i)if(0<=p(t.start,e.end))l(t,e,-1);else{if(!(p(t.end,e.start)<=0))return;l(e,t,-1)}else if(!n&&i)if(0<=p(t.start,e.start))l(t,e,1);else{if(!(p(t.start,e.start)<=0))return;l(e,t,1)}else if(!n&&!i)if(0<=p(t.start,e.start))l(t,e,1);else{if(!(p(t.end,e.start)<=0))return;l(e,t,-1)}return 1}function l(e,t,n){c(e.start,t.start,t.end,n),c(e.end,t.start,t.end,n)}function c(e,t,n,i){e.row==(1==i?t:n).row&&(e.column+=i*(n.column-t.column)),e.row+=i*(n.row-t.row)}function d(e,t){var n=e.lines,i=e.end,r=(e.end=o(t),e.end.row-e.start.row),a=n.splice(r,n.length),r=r?t.column:t.column-e.start.column;return n.push(a[0].substring(0,r)),a[0]=a[0].substr(r),{start:o(t),end:i,lines:a,action:e.action}}i.prototype.addSession=function(e){this.$session=e},i.prototype.add=function(e,t,n){this.$fromUndo||e!=this.$lastDelta&&(this.$keepRedoStack||(this.$redoStack.length=0),!1!==t&&this.lastDeltas||(this.lastDeltas=[],(t=this.$undoStack.length)>this.$undoDepth-1&&this.$undoStack.splice(0,t-this.$undoDepth+1),this.$undoStack.push(this.lastDeltas),e.id=this.$rev=++this.$maxRev),"remove"!=e.action&&"insert"!=e.action||(this.$lastDelta=e),this.lastDeltas.push(e))},i.prototype.addSelection=function(e,t){this.selections.push({value:e,rev:t||this.$rev})},i.prototype.startNewGroup=function(){return this.lastDeltas=null,this.$rev},i.prototype.markIgnored=function(e,t){null==t&&(t=this.$rev+1);for(var n=this.$undoStack,i=n.length;i--;){var o=n[i][0];if(o.id<=e)break;o.idthis.endRow)throw new Error("Can't add a fold to this FoldLine as it has no connection");this.folds.push(e),this.folds.sort(function(e,t){return-e.range.compareEnd(t.start.row,t.start.column)}),0=this.start.row&&e<=this.end.row},i.prototype.walk=function(e,t,n){var i,o,r=0,a=this.folds,s=!0;null==t&&(t=this.end.row,n=this.end.column);for(var l=0;lt||n[n.length-1].start.row=i);a++);if("insert"==e.action)for(var l=o-i,c=-t.column+n.column;ai);a++)d.start.row==i&&d.start.column>=t.column&&(d.start.column==t.column&&this.$bias<=0||(d.start.column+=c,d.start.row+=l)), d.end.row==i&&d.end.column>=t.column&&(d.end.column==t.column&&this.$bias<0||(d.end.column==t.column&&0d.start.column&&d.end.column==r[a+1].start.column&&(d.end.column-=c),d.end.column+=c,d.end.row+=l));else for(var d,l=i-o,c=t.column-n.column;ao);a++)d.end.rowt.column)&&(d.end.column=t.column,d.end.row=t.row):(d.end.column+=c,d.end.row+=l):d.end.row>o&&(d.end.row+=l),d.start.rowt.column)&&(d.start.column=t.column,d.start.row=t.row):(d.start.column+=c,d.start.row+=l):d.start.row>o&&(d.start.row+=l);if(0!=l&&a=e)return o;if(o.end.row>e)return null}return null},this.getNextFoldLine=function(e,t){var n=this.$foldData,i=0;for(-1==(i=t?n.indexOf(t):i)&&(i=0);i=e)return o}return null},this.getFoldedRowCount=function(e,t){for(var n=this.$foldData,i=t-e+1,o=0;oc)break}while(r&&l.test(r.type)&&!/^comment.start/.test(r.type));r=o.stepBackward()}else r=o.getCurrentToken();return s.end.row=o.getCurrentTokenRow(),s.end.column=o.getCurrentTokenColumn(),/^comment.end/.test(r.type)||(s.end.column+=r.value.length-2),s}},this.foldAll=function(e,t,n,i){null==n&&(n=1e5);var o=this.foldWidgets;if(o){t=t||this.getLength();for(var r,a=e=e||0;a=e&&(a=r.end.row,r.collapseChildren=n,this.addFold("...",r))}},this.foldToLevel=function(e){for(this.foldAll();0=e)break}i--}return{range:-1!==i&&r,firstRange:a}},this.onFoldWidgetClick=function(e,t){var n={children:(t=t instanceof s?t.domEvent:t).shiftKey,all:t.ctrlKey||t.metaKey,siblings:t.altKey};this.$toggleFoldWidget(e,n)||(e=t.target||t.srcElement)&&/ace_fold-widget/.test(e.className)&&(e.className+=" ace_invalid")},this.$toggleFoldWidget=function(e,t){var n,i,o,r;if(this.getFoldWidget)return n=this.getFoldWidget(e),i=this.getLine(e),(i=this.getFoldAt(e,-1==(n="end"===n?-1:1)?0:i.length,n))?(t.children||t.all?this.removeFold(i):this.expandFold(i),i):(n=this.getFoldWidgetRange(e,!0))&&!n.isMultiLine()&&(i=this.getFoldAt(n.start.row,n.start.column,1))&&n.isEqual(i.range)?(this.removeFold(i),i):(t.siblings?((i=this.getParentFoldRangeData(e)).range&&(o=i.range.start.row+1,r=i.range.end.row),this.foldAll(o,r,t.all?1e4:0)):t.children?(r=n?n.end.row:this.getLength(),this.foldAll(e+1,r,t.all?1e4:0)):n&&(t.all&&(n.collapseChildren=1e4),this.addFold("...",n)),n)},this.toggleFoldWidget=function(e){var t,n=this.selection.getCursor().row;n=this.getRowFoldStart(n),!this.$toggleFoldWidget(n,{})&&(t=(t=this.getParentFoldRangeData(n,!0)).range||t.firstRange)&&(n=t.start.row,(n=this.getFoldAt(n,this.getLine(n).length,1))?this.removeFold(n):this.addFold("...",t))},this.updateFoldWidgets=function(e){var t=e.start.row,n=e.end.row-t;0==n?this.foldWidgets[t]=null:"remove"==e.action?this.foldWidgets.splice(t,1+n,null):((e=Array(1+n)).unshift(t,1),this.foldWidgets.splice.apply(this.foldWidgets,e))},this.tokenizerUpdateFoldWidgets=function(e){e=e.data,e.first!=e.last&&this.foldWidgets.length>e.first&&this.foldWidgets.splice(e.first,this.foldWidgets.length)}}}),ace.define("ace/edit_session/bracket_match",["require","exports","module","ace/token_iterator","ace/range"],function(e,t,n){"use strict";var i=e("../token_iterator").TokenIterator,o=e("../range").Range;t.BracketMatch=function(){this.findMatchingBracket=function(e,t){return 0!=e.column&&""!=(t=t||this.getLine(e.row).charAt(e.column-1))&&(t=t.match(/([\(\[\{])|([\)\]\}])/))?t[1]?this.$findClosingBracket(t[1],e):this.$findOpeningBracket(t[2],e):null},this.getBracketRange=function(e){var t,n,i=this.getLine(e.row),r=!0,a=i.charAt(e.column-1),s=a&&a.match(/([\(\[\{])|([\)\]\}])/);if(s||(a=i.charAt(e.column),e={row:e.row,column:e.column+1},s=a&&a.match(/([\(\[\{])|([\)\]\}])/),r=!1),!s)return null;if(s[1]){if(!(n=this.$findClosingBracket(s[1],e)))return null;t=o.fromPoints(e,n),r||(t.end.column++,t.start.column--),t.cursor=t.end}else{if(!(n=this.$findOpeningBracket(s[2],e)))return null;t=o.fromPoints(n,e),r||(t.start.column++,t.end.column--),t.cursor=t.start}return t},this.getMatchingBracketRanges=function(e,t){var n=this.getLine(e.row),i=/([\(\[\{])|([\)\]\}])/,r=!t&&n.charAt(e.column-1),a=r&&r.match(i);return a||(r=(void 0===t||t)&&n.charAt(e.column),e={row:e.row,column:e.column+1},a=r&&r.match(i)),a?(t=new o(e.row,e.column-1,e.row,e.column),(n=a[1]?this.$findClosingBracket(a[1],e):this.$findOpeningBracket(a[2],e))?[t,new o(n.row,n.column,n.row,n.column+1)]:[t]):null},this.$brackets={")":"(","(":")","]":"[","[":"]","{":"}","}":"{","<":">",">":"<"},this.$findOpeningBracket=function(e,t,n){var o=this.$brackets[e],r=1,a=new i(this,t.row,t.column),s=a.getCurrentToken();if(s=s||a.stepForward()){n=n||new RegExp("(\\.?"+s.type.replace(".","\\.").replace("rparen",".paren").replace(/\b(?:end)\b/,"(?:start|begin|end)").replace(/-close\b/,"-(close|open)")+")+");for(var l=t.column-a.getCurrentTokenColumn()-2,c=s.value;;){for(;0<=l;){var d=c.charAt(l);if(d==o){if(0==--r)return{row:a.getCurrentTokenRow(),column:l+a.getCurrentTokenColumn()}}else d==e&&(r+=1);--l}for(;(s=a.stepBackward())&&!n.test(s.type););if(null==s)break;l=(c=s.value).length-1}return null}},this.$findClosingBracket=function(e,t,n){var o=this.$brackets[e],r=1,a=new i(this,t.row,t.column),s=a.getCurrentToken();if(s=s||a.stepForward()){n=n||new RegExp("(\\.?"+s.type.replace(".","\\.").replace("lparen",".paren").replace(/\b(?:start|begin)\b/,"(?:start|begin|end)").replace(/-open\b/,"-(close|open)")+")+");for(var l=t.column-a.getCurrentTokenColumn();;){for(var c=s.value,d=c.length;l"===t.value?i=!0:-1!==t.type.indexOf("tag-name")&&(n=!0)),t&&!n;);return t},this.$findClosingTag=function(e,t){var n=t.value,i=t.value,r=0,a=new o(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+1),s=(t=e.stepForward(),new o(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+t.value.length)),l=!1;do{var c,d=t;if(t=e.stepForward())if(">"!==t.value||l||(c=new o(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+1),l=!0),-1!==t.type.indexOf("tag-name")){if(i===(n=t.value))if("<"===d.value)r++;else if(""!==t.value)return;var p=new o(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+1)}}else i===n&&"/>"===t.value&&--r<0&&(p=h=u=new o(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+2),c=new o(s.end.row,s.end.column,s.end.row,s.end.column+1))}while(t&&0<=r);if(a&&c&&u&&p&&s&&h)return{openTag:new o(a.start.row,a.start.column,c.end.row,c.end.column),closeTag:new o(u.start.row,u.start.column,p.end.row,p.end.column),openTagName:s,closeTagName:h}},this.$findOpeningTag=function(e,t){var n,i=e.getCurrentToken(),r=t.value,a=0,s=e.getCurrentTokenRow(),l=e.getCurrentTokenColumn(),c=new o(s,l,s,l+2),d=(e.stepForward(),new o(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+t.value.length));if((t=e.stepForward())&&">"===t.value){var u=new o(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+1);e.stepBackward(),e.stepBackward();do{if(t=i,s=e.getCurrentTokenRow(),n=(l=e.getCurrentTokenColumn())+t.value.length,i=e.stepBackward(),t)if(-1!==t.type.indexOf("tag-name")){if(r===t.value)if("<"===i.value){if(0<++a){for(var h=new o(s,l,s,n),p=new o(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+1);(t=e.stepForward())&&">"!==t.value;);var f=new o(e.getCurrentTokenRow(),e.getCurrentTokenColumn(),e.getCurrentTokenRow(),e.getCurrentTokenColumn()+1)}}else""===t.value){for(var g=0,m=i;m;){if(-1!==m.type.indexOf("tag-name")&&m.value===r){a--;break}if("<"===m.value)break;m=e.stepBackward(),g++}for(var y=0;y>1,r=e[o];if(re&&(e=t.screenWidth)}),this.lineWidgetWidth=e)},i.prototype.$computeWidth=function(e){if(this.$modified||e){if(this.$modified=!1,this.$useWrapMode)return this.screenWidth=this.$wrapLimit @@ -27,26 +27,26 @@ scrollerHeight:a.scrollerHeight,scrollerWidth:a.scrollerWidth};return i&&(e||a.h var e=c.shift();e.command?i[e.command].apply(i,e.args):e.event&&a._signal(e.event,e.data)});return a.postMessage=function(e){d.onMessage({data:e})},a.callback=function(e,t){this.postMessage({type:"call",id:t,data:e})},a.emit=function(e,t){this.postMessage({type:"event",name:e,data:t})},l.loadModule(["worker",t],function(e){for(i=new e[n](a);c.length;)u()}),d},t.WorkerClient=o,t.createWorker=i}),ace.define("ace/placeholder",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/oop"],function(e,t,n){"use strict";function i(e,t,n,i,o,r){var a=this,t=(this.length=t,this.session=e,this.doc=e.getDocument(),this.mainClass=o,this.othersClass=r,this.$onUpdate=this.onUpdate.bind(this),this.doc.on("change",this.$onUpdate,!0),this.$others=i,this.$onCursorChange=function(){setTimeout(function(){a.onCursorChange()})},this.$pos=n,e.getUndoManager().$undoStack||e.getUndoManager().$undostack||{length:-1});this.$undoStackDepth=t.length,this.setup(),e.selection.on("changeCursor",this.$onCursorChange)}var o=e("./range").Range,r=e("./lib/event_emitter").EventEmitter,e=e("./lib/oop"),a=(i.prototype.setup=function(){var e=this,t=this.doc,n=this.session,i=(this.selectionBefore=n.selection.toJSON(),n.selection.inMultiSelectMode&&n.selection.toSingleRange(),this.pos=t.createAnchor(this.$pos.row,this.$pos.column),this.pos);i.$insertRight=!0,i.detach(),i.markerId=n.addMarker(new o(i.row,i.column,i.row,i.column+this.length),this.mainClass,null,!1),this.others=[],this.$others.forEach(function(n){n=t.createAnchor(n.row,n.column),n.$insertRight=!0,n.detach(),e.others.push(n)}),n.setUndoSelect(!1)},i.prototype.showOtherMarkers=function(){var e,t;this.othersActive||(e=this.session,(t=this).othersActive=!0,this.others.forEach(function(n){n.markerId=e.addMarker(new o(n.row,n.column,n.row,n.column+t.length),t.othersClass,null,!1)}))},i.prototype.hideOtherMarkers=function(){if(this.othersActive){this.othersActive=!1;for(var e=0;e=this.pos.column&&t.start.column<=this.pos.column+this.length+1,r=t.start.column-this.pos.column;if(this.updateAnchors(e),i&&(this.length+=n),i&&!this.session.$fromUndo)if("insert"===e.action)for(var a=this.others.length-1;0<=a;a--){var s={row:(l=this.others[a]).row,column:l.column+r};this.doc.insertMergedLines(s,e.lines)}else if("remove"===e.action)for(a=this.others.length-1;0<=a;a--){var l,s={row:(l=this.others[a]).row,column:l.column+r};this.doc.remove(new o(s.row,s.column,s.row,s.column-n))}this.$updating=!1,this.updateMarkers()}},i.prototype.updateAnchors=function(e){this.pos.onChange(e);for(var t=this.others.length;t--;)this.others[t].onChange(e);this.updateMarkers()},i.prototype.updateMarkers=function(){if(!this.$updating){var e=this,t=this.session,n=function(n,i){t.removeMarker(n.markerId),n.markerId=t.addMarker(new o(n.row,n.column,n.row,n.column+e.length),i,null,!1)};n(this.pos,this.mainClass);for(var i=this.others.length;i--;)n(this.others[i],this.othersClass)}},i.prototype.onCursorChange=function(e){var t;!this.$updating&&this.session&&((t=this.session.selection.getCursor()).row===this.pos.row&&t.column>=this.pos.column&&t.column<=this.pos.column+this.length?(this.showOtherMarkers(),this._emit("cursorEnter",e)):(this.hideOtherMarkers(),this._emit("cursorLeave",e)))},i.prototype.detach=function(){this.session.removeMarker(this.pos&&this.pos.markerId),this.hideOtherMarkers(),this.doc.off("change",this.$onUpdate),this.session.selection.off("changeCursor",this.$onCursorChange),this.session.setUndoSelect(!0),this.session=null},i.prototype.cancel=function(){if(-1!==this.$undoStackDepth){for(var e=this.session.getUndoManager(),t=(e.$undoStack||e.$undostack).length-this.$undoStackDepth,n=0;na&&(a=t.column),(n=-1==n?0:n)t[1].length&&(o=t[1].length),rt[3].length&&(a=t[3].length)),t):[e]}).map(t?i:s?l?function(e){return e[2]?n(o+r-e[2].length)+e[2]+n(a)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}:i:function(e){return e[2]?n(o)+e[2]+n(a)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]})}}).call(h.prototype),t.onSessionChange=function(e){var t=e.session,e=(t&&!t.multiSelect&&(t.$selectionMarkers=[],t.selection.$initRangeList(),t.multiSelect=t.selection),this.multiSelect=t&&t.multiSelect,e.oldSession);e&&(e.multiSelect.off("addRange",this.$onAddRange),e.multiSelect.off("removeRange",this.$onRemoveRange),e.multiSelect.off("multiSelect",this.$onMultiSelect),e.multiSelect.off("singleSelect",this.$onSingleSelect),e.multiSelect.lead.off("change",this.$checkMultiselectChange),e.multiSelect.anchor.off("change",this.$checkMultiselectChange)),t&&(t.multiSelect.on("addRange",this.$onAddRange),t.multiSelect.on("removeRange",this.$onRemoveRange),t.multiSelect.on("multiSelect",this.$onMultiSelect),t.multiSelect.on("singleSelect",this.$onSingleSelect),t.multiSelect.lead.on("change",this.$checkMultiselectChange),t.multiSelect.anchor.on("change",this.$checkMultiselectChange)),t&&this.inMultiSelectMode!=t.selection.inMultiSelectMode&&(t.selection.inMultiSelectMode?this.$onMultiSelect():this.$onSingleSelect())},t.MultiSelect=i,e("./config").defineOptions(h.prototype,"editor",{enableMultiselect:{set:function(e){i(this),e?this.on("mousedown",s):this.off("mousedown",s)},value:!0},enableBlockSelect:{set:function(e){this.$blockSelectEnabled=e},value:!0}})}),ace.define("ace/mode/folding/fold_mode",["require","exports","module","ace/range"],function(e,t,n){"use strict";var i=e("../../range").Range,e=t.FoldMode=function(){};(function(){this.foldingStartMarker=null,this.foldingStopMarker=null,this.getFoldWidget=function(e,t,n){return e=e.getLine(n),this.foldingStartMarker.test(e)?"start":"markbeginend"==t&&this.foldingStopMarker&&this.foldingStopMarker.test(e)?"end":""},this.getFoldWidgetRange=function(e,t,n){return null},this.indentationBlock=function(e,t,n){var o=/\S/,r=e.getLine(t),a=r.search(o);if(-1!=a){for(var s,n=n||r.length,l=e.getLength(),r=t,c=t;++tn.row&&(o.row--,o.column=e.getLine(o.row).length),i.fromPoints(n,o)},this.closingBracketBlock=function(e,t,n,o,r){if(n={row:n,column:o},o=e.$findOpeningBracket(t,n))return o.column++,n.column--,i.fromPoints(o,n)}}).call(e.prototype)}),ace.define("ace/ext/error_marker",["require","exports","module","ace/line_widgets","ace/lib/dom","ace/range","ace/config"],function(e,t,n){"use strict";function i(e,t,n){var i=e.getAnnotations().sort(a.comparePoints);if(i.length){var o=function(e,t,n){for(var i=0,o=e.length-1;i<=o;){var r=i+o>>1,a=n(t,e[r]);if(0=i.length?o=0"),d.appendChild(r.createElement("div")),h.destroy=function(){e.$mouseHandler.isMousePressed||(e.keyBinding.removeKeyboardHandler(n),l.widgetManager.removeLineWidget(h),e.off("changeSelection",h.destroy),e.off("changeSession",h.destroy),e.off("mouseup",h.destroy),e.off("change",h.destroy))},e.keyBinding.addKeyboardHandler(n),e.on("changeSelection",h.destroy),e.on("changeSession",h.destroy),e.on("mouseup",h.destroy),e.on("change",h.destroy),e.session.widgetManager.addLineWidget(h),h.el.onmousedown=e.focus.bind(e),e.renderer.scrollCursorIntoView(null,.5,{bottom:h.el.offsetHeight})},r.importCssString("\n .error_widget_wrapper {\n background: inherit;\n color: inherit;\n border:none\n }\n .error_widget {\n border-top: solid 2px;\n border-bottom: solid 2px;\n margin: 5px 0;\n padding: 10px 40px;\n white-space: pre-wrap;\n }\n .error_widget.ace_error, .error_widget_arrow.ace_error{\n border-color: #ff5a5a\n }\n .error_widget.ace_warning, .error_widget_arrow.ace_warning{\n border-color: #F1D817\n }\n .error_widget.ace_info, .error_widget_arrow.ace_info{\n border-color: #5a5a5a\n }\n .error_widget.ace_ok, .error_widget_arrow.ace_ok{\n border-color: #5aaa5a\n }\n .error_widget_arrow {\n position: absolute;\n border: solid 5px;\n border-top-color: transparent!important;\n border-right-color: transparent!important;\n border-left-color: transparent!important;\n top: -5px;\n }\n","error_marker.css",!1)}),ace.define("ace/ace",["require","exports","module","ace/lib/dom","ace/range","ace/editor","ace/edit_session","ace/undomanager","ace/virtual_renderer","ace/worker/worker_client","ace/keyboard/hash_handler","ace/placeholder","ace/multi_select","ace/mode/folding/fold_mode","ace/theme/textmate","ace/ext/error_marker","ace/config","ace/loader_build"],function(e,t,n){"use strict";e("./loader_build")(t);var i=e("./lib/dom"),o=e("./range").Range,r=e("./editor").Editor,a=e("./edit_session").EditSession,s=e("./undomanager").UndoManager,l=e("./virtual_renderer").VirtualRenderer;e("./worker/worker_client"),e("./keyboard/hash_handler"),e("./placeholder"),e("./multi_select"),e("./mode/folding/fold_mode"),e("./theme/textmate"),e("./ext/error_marker"),t.config=e("./config"),t.edit=function(e,n){if("string"==typeof e){var o=e;if(!(e=document.getElementById(o)))throw new Error("ace.edit can't find div #"+o)}var a,s;return e&&e.env&&e.env.editor instanceof r?e.env.editor:(o="",e&&/input|textarea/i.test(e.tagName)?(o=(a=e).value,e=i.createElement("pre"),a.parentNode.replaceChild(e,a)):e&&(o=e.textContent,e.innerHTML=""),o=t.createEditSession(o),e=new r(new l(e),o,n),s={document:o,editor:e,onResize:e.resize.bind(e,null)},a&&(s.textarea=a),e.on("destroy",function(){s.editor.container.env=null}),e.container.env=e.env=s,e)},t.createEditSession=function(e,t){return e=new a(e,t),e.setUndoManager(new s),e},t.Range=o,t.Editor=r,t.EditSession=a,t.UndoManager=s,t.VirtualRenderer=l,t.version=t.config.version}),ace.require(["ace/ace"],function(t){t&&(t.config.init(!0),t.define=ace.define);var n,i=function(){return this}();for(n in(i=(i=i||"undefined"==typeof window?i:window)||"undefined"==typeof self?i:self).ace||(i.ace=t),t)t.hasOwnProperty(n)&&(i.ace[n]=t[n]);i.ace.default=i.ace,e&&(e.exports=i.ace)})},6489:function(e,t,n){e=n.nmd(e),ace.define("ace/snippets",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/event_emitter","ace/lib/lang","ace/range","ace/range_list","ace/keyboard/hash_handler","ace/tokenizer","ace/clipboard","ace/editor"],function(e,t,n){"use strict";function i(e){return e=(new Date).toLocaleString("en-us",e),1==e.length?"0"+e:e}function o(){this.snippetMap={},this.snippetNameMap={},this.variables=g}function r(e){if(this.index=0,this.ranges=[],this.tabstops=[],e.tabstopManager)return e.tabstopManager;(e.tabstopManager=this).$onChange=this.onChange.bind(this),this.$onChangeSelection=c.delayedCall(this.onChangeSelection.bind(this)).schedule,this.$onChangeSession=this.onChangeSession.bind(this),this.$onAfterExec=this.onAfterExec.bind(this),this.attach(e)}var a=e("./lib/dom"),s=e("./lib/oop"),l=e("./lib/event_emitter").EventEmitter,c=e("./lib/lang"),d=e("./range").Range,u=e("./range_list").RangeList,h=e("./keyboard/hash_handler").HashHandler,p=e("./tokenizer").Tokenizer,f=e("./clipboard"),g={CURRENT_WORD:function(e){return e.session.getTextRange(e.session.getWordRange())},SELECTION:function(e,t,n){return e=e.session.getTextRange(),n?e.replace(/\n\r?([ \t]*\S)/g,"\n"+n+"$1"):e},CURRENT_LINE:function(e){return e.session.getLine(e.getCursorPosition().row)},PREV_LINE:function(e){return e.session.getLine(e.getCursorPosition().row-1)},LINE_INDEX:function(e){return e.getCursorPosition().row},LINE_NUMBER:function(e){return e.getCursorPosition().row+1},SOFT_TABS:function(e){return e.session.getUseSoftTabs()?"YES":"NO"},TAB_SIZE:function(e){return e.session.getTabSize()},CLIPBOARD:function(e){return f.getText&&f.getText()},FILENAME:function(e){return/[^/\\]*$/.exec(this.FILEPATH(e))[0]},FILENAME_BASE:function(e){ return/[^/\\]*$/.exec(this.FILEPATH(e))[0].replace(/\.[^.]*$/,"")},DIRECTORY:function(e){return this.FILEPATH(e).replace(/[^/\\]*$/,"")},FILEPATH:function(e){return"/not implemented.txt"},WORKSPACE_NAME:function(){return"Unknown"},FULLNAME:function(){return"Unknown"},BLOCK_COMMENT_START:function(e){return e=e.session.$mode||{},e.blockComment&&e.blockComment.start||""},BLOCK_COMMENT_END:function(e){return e=e.session.$mode||{},e.blockComment&&e.blockComment.end||""},LINE_COMMENT:function(e){return(e.session.$mode||{}).lineCommentStart||""},CURRENT_YEAR:i.bind(null,{year:"numeric"}),CURRENT_YEAR_SHORT:i.bind(null,{year:"2-digit"}),CURRENT_MONTH:i.bind(null,{month:"numeric"}),CURRENT_MONTH_NAME:i.bind(null,{month:"long"}),CURRENT_MONTH_NAME_SHORT:i.bind(null,{month:"short"}),CURRENT_DATE:i.bind(null,{day:"2-digit"}),CURRENT_DAY_NAME:i.bind(null,{weekday:"long"}),CURRENT_DAY_NAME_SHORT:i.bind(null,{weekday:"short"}),CURRENT_HOUR:i.bind(null,{hour:"2-digit",hour12:!1}),CURRENT_MINUTE:i.bind(null,{minute:"2-digit"}),CURRENT_SECOND:i.bind(null,{second:"2-digit"})};g.SELECTED_TEXT=g.SELECTION,o.prototype.getTokenizer=function(){return o.$tokenizer||this.createTokenizer()},o.prototype.createTokenizer=function(){function e(e){return e=e.substr(1),/^\d+$/.test(e)?[{tabstopId:parseInt(e,10)}]:[{text:e}]}function t(e){return"(?:[^\\\\"+e+"]|\\\\.)"}var n={regex:"/("+t("/")+"+)/",onMatch:function(e,t,n){return n=n[0],n.fmtString=!0,n.guard=e.slice(1,-1),n.flag=""},next:"formatString"};return o.$tokenizer=new p({start:[{regex:/\\./,onMatch:function(e,t,n){var i=e[1];return[e="}"==i&&n.length||-1!="`$\\".indexOf(i)?i:e]}},{regex:/}/,onMatch:function(e,t,n){return[n.length?n.shift():e]}},{regex:/\$(?:\d+|\w+)/,onMatch:e},{regex:/\$\{[\dA-Z_a-z]+/,onMatch:function(t,n,i){return t=e(t.substr(1)),i.unshift(t[0]),t},next:"snippetVar"},{regex:/\n/,token:"newline",merge:!1}],snippetVar:[{regex:"\\|"+t("\\|")+"*\\|",onMatch:function(e,t,n){return e=e.slice(1,-1).replace(/\\[,|\\]|,/g,function(e){return 2==e.length?e[1]:"\0"}).split("\0").map(function(e){return{value:e}}),[(n[0].choices=e)[0]]},next:"start"},n,{regex:"([^:}\\\\]|\\\\.)*:?",token:"",next:"start"}],formatString:[{regex:/:/,onMatch:function(e,t,n){return n.length&&n[0].expectElse?(n[0].expectElse=!1,n[0].ifEnd={elseEnd:n[0]},[n[0].ifEnd]):":"}},{regex:/\\./,onMatch:function(e,t,n){var i=e[1];return"}"==i&&n.length||-1!="`$\\".indexOf(i)?e=i:"n"==i?e="\n":"t"==i?e="\t":-1!="ulULE".indexOf(i)&&(e={changeCase:i,local:"a"l&&(d=l-o.offsetWidth),o.style.left=d+"px",o.style.right="",s.isOpen||(s.isOpen=!0,this._signal("show"),r=null),s.anchorPos=e,s.anchor=n}return!0},s.show=function(e,t,n){this.tryShow(e,t,n?"bottom":void 0,!0)},s.goTo=function(e){var t=this.getRow(),n=this.session.getLength()-1;switch(e){case"up":t=t<=0?n:t-1;break;case"down":t=n<=t?-1:t+1;break;case"start":t=0;break;case"end":t=n}this.setRow(t)},s.getTextLeftOffset=function(){return this.$borderSize+this.renderer.$padding+this.$imageSize},s.$imageSize=0,s.$borderSize=1,s}var a=e("../virtual_renderer").VirtualRenderer,s=e("../editor").Editor,l=e("../range").Range,c=e("../lib/event"),d=e("../lib/lang"),u=e("../lib/dom"),h=e("../config").nls,e=e("./../lib/useragent"),p=e.isSafari?"menu":"listbox",f=e.isSafari?"menuitem":"option",g=e.isSafari?"aria-current":"aria-selected";u.importCssString('\n.ace_editor.ace_autocomplete .ace_marker-layer .ace_active-line {\n background-color: #CAD6FA;\n z-index: 1;\n}\n.ace_dark.ace_editor.ace_autocomplete .ace_marker-layer .ace_active-line {\n background-color: #3a674e;\n}\n.ace_editor.ace_autocomplete .ace_line-hover {\n border: 1px solid #abbffe;\n margin-top: -1px;\n background: rgba(233,233,253,0.4);\n position: absolute;\n z-index: 2;\n}\n.ace_dark.ace_editor.ace_autocomplete .ace_line-hover {\n border: 1px solid rgba(109, 150, 13, 0.8);\n background: rgba(58, 103, 78, 0.62);\n}\n.ace_completion-meta {\n opacity: 0.5;\n margin-left: 0.9em;\n}\n.ace_completion-message {\n margin-left: 0.9em;\n color: blue;\n}\n.ace_editor.ace_autocomplete .ace_completion-highlight{\n color: #2d69c7;\n}\n.ace_dark.ace_editor.ace_autocomplete .ace_completion-highlight{\n color: #93ca12;\n}\n.ace_editor.ace_autocomplete {\n width: 300px;\n z-index: 200000;\n border: 1px lightgray solid;\n position: fixed;\n box-shadow: 2px 3px 5px rgba(0,0,0,.2);\n line-height: 1.4;\n background: #fefefe;\n color: #111;\n}\n.ace_dark.ace_editor.ace_autocomplete {\n border: 1px #484747 solid;\n box-shadow: 2px 3px 5px rgba(0, 0, 0, 0.51);\n line-height: 1.4;\n background: #25282c;\n color: #c1c1c1;\n}\n.ace_autocomplete .ace_text-layer {\n width: calc(100% - 8px);\n}\n.ace_autocomplete .ace_line {\n display: flex;\n align-items: center;\n}\n.ace_autocomplete .ace_line > * {\n min-width: 0;\n flex: 0 0 auto;\n}\n.ace_autocomplete .ace_line .ace_ {\n flex: 0 1 auto;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n.ace_autocomplete .ace_completion-spacer {\n flex: 1;\n}\n.ace_autocomplete.ace_loading:after {\n content: "";\n position: absolute;\n top: 0px;\n height: 2px;\n width: 8%;\n background: blue;\n z-index: 100;\n animation: ace_progress 3s infinite linear;\n animation-delay: 300ms;\n transform: translateX(-100%) scaleX(1);\n}\n@keyframes ace_progress {\n 0% { transform: translateX(-100%) scaleX(1) }\n 50% { transform: translateX(625%) scaleX(2) } \n 100% { transform: translateX(1500%) scaleX(3) } \n}\n@media (prefers-reduced-motion) {\n .ace_autocomplete.ace_loading:after {\n transform: translateX(625%) scaleX(2);\n animation: none;\n }\n}\n',"autocompletion.css",!1),t.AcePopup=r,t.$singleLineEditor=o,t.getAriaId=i}),ace.define("ace/autocomplete/inline_screenreader",["require","exports","module"],function(e,t,n){"use strict";function i(e){this.editor=e,this.screenReaderDiv=document.createElement("div"),this.screenReaderDiv.classList.add("ace_screenreader-only"),this.editor.container.appendChild(this.screenReaderDiv)}i.prototype.setScreenReaderContent=function(e){for(!this.popup&&this.editor.completer&&this.editor.completer.popup&&(this.popup=this.editor.completer.popup,this.popup.renderer.on("afterRender",function(){var e=this.popup.getRow(),t=this.popup.renderer.$textLayer,e=t.element.childNodes[e-t.config.firstRow];if(e){for(var n="doc-tooltip ",i=0;ithis.filterText&&0===e.lastIndexOf(this.filterText,0)?this.filtered:this.all,this.filterText=e,t=(t=this.filterCompletions(t,this.filterText)).sort(function(e,t){return t.exactMatch-e.exactMatch||t.$score-e.$score||(e.caption||e.value).localeCompare(t.caption||t.value)});var t,n=null;t=t.filter(function(e){return(e=e.snippet||e.caption||e.value)!==n&&(n=e,!0)}),this.filtered=t},a.prototype.filterCompletions=function(e,t){var n=[],i=t.toUpperCase(),o=t.toLowerCase();e:for(var r,a=0;r=e[a];a++){var s=!this.ignoreCaption&&r.caption||r.value||r.snippet;if(s){var l=-1,c=0,d=0;if(this.exactMatch){if(t!==s.substr(0,t.length))continue}else{var u=s.toLowerCase().indexOf(o);if(-1",c.escapeHTML(e.caption),"","
    ",c.escapeHTML((e=e.snippet,t={},e.replace(/\${(\d+)(:(.*?))?}/g,function(e,n,i,o){return t[n]=o||""}).replace(/\$(\d+?)/g,function(e,n){return t[n]})))].join(""))},id:"snippetCompleter"},f=[p,u,h],g=(t.setCompleters=function(e){f.length=0,e&&f.push.apply(f,e)},t.addCompleter=function(e){f.push(e)},t.textCompleter=u,t.keyWordCompleter=h,t.snippetCompleter=p,{name:"expandSnippet",exec:function(e){return a.expandWithTab(e)},bindKey:"Tab"}),m=function e(t){(t="string"==typeof t?l.$modes[t]:t)&&(a.files||(a.files={}),y(t.$id,t.snippetFileId),t.modes)&&t.modes.forEach(e)},y=function(e,t){t&&e&&!a.files[e]&&(a.files[e]={},l.loadModule(t,function(t){t&&(!(a.files[e]=t).snippets&&t.snippetText&&(t.snippets=a.parseSnippetFile(t.snippetText)),a.register(t.snippets||[],t.scope),t.includeScopes)&&(a.snippetMap[t.scope].includeScopes=t.includeScopes,t.includeScopes.forEach(function(e){m("ace/mode/"+e)}))}))},v=c.delayedCall(function(){b(r)},0),b=function(e){var e=e.editor,t=d.getCompletionPrefix(e),n=d.triggerAutocomplete(e);(t&&t.length>=e.$liveAutocompletionThreshold||n)&&((t=s.for(e)).autoShown=!0,t.showPopup(e))},u=e("../editor").Editor;e("../config").defineOptions(u.prototype,"editor",{enableBasicAutocompletion:{set:function(e){e?(this.completers||(this.completers=Array.isArray(e)?e:f),this.commands.addCommand(s.startCommand)):this.commands.removeCommand(s.startCommand)},value:!1},enableLiveAutocompletion:{set:function(e){e?(this.completers||(this.completers=Array.isArray(e)?e:f),this.commands.on("afterExec",o)):this.commands.off("afterExec",o)},value:!1},liveAutocompletionDelay:{initialValue:0},liveAutocompletionThreshold:{initialValue:0},enableSnippets:{set:function(e){e?(this.commands.addCommand(g),this.on("changeMode",i),i(0,this)):(this.commands.removeCommand(g),this.off("changeMode",i))},value:!1}})}),ace.require(["ace/ext/language_tools"],function(t){e&&(e.exports=t)})},6534:function(e,t,n){e=n.nmd(e),ace.define("ace/ext/searchbox-css",["require","exports","module"],function(e,t,n){n.exports='\n\n/* ------------------------------------------------------------------------------------------\n * Editor Search Form\n * --------------------------------------------------------------------------------------- */\n.ace_search {\n background-color: #ddd;\n color: #666;\n border: 1px solid #cbcbcb;\n border-top: 0 none;\n overflow: hidden;\n margin: 0;\n padding: 4px 6px 0 4px;\n position: absolute;\n top: 0;\n z-index: 99;\n white-space: normal;\n}\n.ace_search.left {\n border-left: 0 none;\n border-radius: 0px 0px 5px 0px;\n left: 0;\n}\n.ace_search.right {\n border-radius: 0px 0px 0px 5px;\n border-right: 0 none;\n right: 0;\n}\n\n.ace_search_form, .ace_replace_form {\n margin: 0 20px 4px 0;\n overflow: hidden;\n line-height: 1.9;\n}\n.ace_replace_form {\n margin-right: 0;\n}\n.ace_search_form.ace_nomatch {\n outline: 1px solid red;\n}\n\n.ace_search_field {\n border-radius: 3px 0 0 3px;\n background-color: white;\n color: black;\n border: 1px solid #cbcbcb;\n border-right: 0 none;\n outline: 0;\n padding: 0;\n font-size: inherit;\n margin: 0;\n line-height: inherit;\n padding: 0 6px;\n min-width: 17em;\n vertical-align: top;\n min-height: 1.8em;\n box-sizing: content-box;\n}\n.ace_searchbtn {\n border: 1px solid #cbcbcb;\n line-height: inherit;\n display: inline-block;\n padding: 0 6px;\n background: #fff;\n border-right: 0 none;\n border-left: 1px solid #dcdcdc;\n cursor: pointer;\n margin: 0;\n position: relative;\n color: #666;\n}\n.ace_searchbtn:last-child {\n border-radius: 0 3px 3px 0;\n border-right: 1px solid #cbcbcb;\n}\n.ace_searchbtn:disabled {\n background: none;\n cursor: default;\n}\n.ace_searchbtn:hover {\n background-color: #eef1f6;\n}\n.ace_searchbtn.prev, .ace_searchbtn.next {\n padding: 0px 0.7em\n}\n.ace_searchbtn.prev:after, .ace_searchbtn.next:after {\n content: "";\n border: solid 2px #888;\n width: 0.5em;\n height: 0.5em;\n border-width: 2px 0 0 2px;\n display:inline-block;\n transform: rotate(-45deg);\n}\n.ace_searchbtn.next:after {\n border-width: 0 2px 2px 0 ;\n}\n.ace_searchbtn_close {\n background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAcCAYAAABRVo5BAAAAZ0lEQVR42u2SUQrAMAhDvazn8OjZBilCkYVVxiis8H4CT0VrAJb4WHT3C5xU2a2IQZXJjiQIRMdkEoJ5Q2yMqpfDIo+XY4k6h+YXOyKqTIj5REaxloNAd0xiKmAtsTHqW8sR2W5f7gCu5nWFUpVjZwAAAABJRU5ErkJggg==) no-repeat 50% 0;\n border-radius: 50%;\n border: 0 none;\n color: #656565;\n cursor: pointer;\n font: 16px/16px Arial;\n padding: 0;\n height: 14px;\n width: 14px;\n top: 9px;\n right: 7px;\n position: absolute;\n}\n.ace_searchbtn_close:hover {\n background-color: #656565;\n background-position: 50% 100%;\n color: white;\n}\n\n.ace_button {\n margin-left: 2px;\n cursor: pointer;\n -webkit-user-select: none;\n -moz-user-select: none;\n -o-user-select: none;\n -ms-user-select: none;\n user-select: none;\n overflow: hidden;\n opacity: 0.7;\n border: 1px solid rgba(100,100,100,0.23);\n padding: 1px;\n box-sizing: border-box!important;\n color: black;\n}\n\n.ace_button:hover {\n background-color: #eee;\n opacity:1;\n}\n.ace_button:active {\n background-color: #ddd;\n}\n\n.ace_button.checked {\n border-color: #3399ff;\n opacity:1;\n}\n\n.ace_search_options{\n margin-bottom: 3px;\n text-align: right;\n -webkit-user-select: none;\n -moz-user-select: none;\n -o-user-select: none;\n -ms-user-select: none;\n user-select: none;\n clear: both;\n}\n\n.ace_search_counter {\n float: left;\n font-family: arial;\n padding: 0 8px;\n}'}),ace.define("ace/ext/searchbox",["require","exports","module","ace/lib/dom","ace/lib/lang","ace/lib/event","ace/ext/searchbox-css","ace/keyboard/hash_handler","ace/lib/keys","ace/config"],function(e,t,n){"use strict";function i(e,t,n){this.activeInput;var i=o.createElement("div");o.buildDom(["div",{class:"ace_search right"},["span",{action:"hide",class:"ace_searchbtn_close"}],["div",{class:"ace_search_form"},["input",{class:"ace_search_field",placeholder:d("Search for"),spellcheck:"false"}],["span",{action:"findPrev",class:"ace_searchbtn prev"},"​"],["span",{action:"findNext",class:"ace_searchbtn next"},"​"],["span",{action:"findAll",class:"ace_searchbtn",title:"Alt-Enter"},d("All")]],["div",{class:"ace_replace_form"},["input",{class:"ace_search_field",placeholder:d("Replace with"),spellcheck:"false"}],["span",{action:"replaceAndFindNext",class:"ace_searchbtn"},d("Replace")],["span",{action:"replaceAll",class:"ace_searchbtn"},d("All")]],["div",{class:"ace_search_options"},["span",{action:"toggleReplace",class:"ace_button",title:d("Toggle Replace mode"),style:"float:left;margin-top:-2px;padding:0 5px;"},"+"],["span",{class:"ace_search_counter"}],["span",{action:"toggleRegexpMode",class:"ace_button",title:d("RegExp Search")},".*"],["span",{action:"toggleCaseSensitive",class:"ace_button",title:d("CaseSensitive Search")},"Aa"],["span",{action:"toggleWholeWords",class:"ace_button",title:d("Whole Word Search")},"\\b"],["span",{action:"searchInSelection",class:"ace_button",title:d("Search In Selection")},"S"]]],i),this.element=i.firstChild,this.setSession=this.setSession.bind(this),this.$init(),this.setEditor(e),o.importCssString(s,"ace_searchbox",e.container)}var o=e("../lib/dom"),r=e("../lib/lang"),a=e("../lib/event"),s=e("./searchbox-css"),l=e("../keyboard/hash_handler").HashHandler,c=e("../lib/keys"),d=e("../config").nls,u=(o.importCssString(s,"ace_searchbox",!1),i.prototype.setEditor=function(e){e.searchBox=this,e.renderer.scroller.appendChild(this.element),this.editor=e},i.prototype.setSession=function(e){this.searchRange=null,this.$syncOptions(!0)},i.prototype.$initElements=function(e){this.searchBox=e.querySelector(".ace_search_form"),this.replaceBox=e.querySelector(".ace_replace_form"),this.searchOption=e.querySelector("[action=searchInSelection]"),this.replaceOption=e.querySelector("[action=toggleReplace]"),this.regExpOption=e.querySelector("[action=toggleRegexpMode]"),this.caseSensitiveOption=e.querySelector("[action=toggleCaseSensitive]"),this.wholeWordOption=e.querySelector("[action=toggleWholeWords]"),this.searchInput=this.searchBox.querySelector(".ace_search_field"),this.replaceInput=this.replaceBox.querySelector(".ace_search_field"),this.searchCounter=e.querySelector(".ace_search_counter")},i.prototype.$init=function(){var e=this.element,t=(this.$initElements(e),this);a.addListener(e,"mousedown",function(e){setTimeout(function(){t.activeInput.focus()},0),a.stopPropagation(e)}),a.addListener(e,"click",function(e){var n=(e.target||e.srcElement).getAttribute("action");n&&t[n]?t[n]():t.$searchBarKb.commands[n]&&t.$searchBarKb.commands[n].exec(t),a.stopPropagation(e)}),a.addCommandKeyListener(e,function(e,n,i){i=c.keyCodeToString(i),n=t.$searchBarKb.findKeyCommand(n,i),n&&n.exec&&(n.exec(t),a.stopEvent(e))}),this.$onChange=r.delayedCall(function(){t.find(!1,!1)}),a.addListener(this.searchInput,"input",function(){t.$onChange.schedule(20)}),a.addListener(this.searchInput,"focus",function(){t.activeInput=t.searchInput,t.searchInput.value&&t.highlight()}),a.addListener(this.replaceInput,"focus",function(){t.activeInput=t.replaceInput,t.searchInput.value&&t.highlight()})},i.prototype.setSearchRange=function(e){(this.searchRange=e)?this.searchRangeMarker=this.editor.session.addMarker(e,"ace_active-line"):this.searchRangeMarker&&(this.editor.session.removeMarker(this.searchRangeMarker),this.searchRangeMarker=null)},i.prototype.$syncOptions=function(e){o.setCssClass(this.replaceOption,"checked",this.searchRange),o.setCssClass(this.searchOption,"checked",this.searchOption.checked),this.replaceOption.textContent=this.replaceOption.checked?"-":"+",o.setCssClass(this.regExpOption,"checked",this.regExpOption.checked),o.setCssClass(this.wholeWordOption,"checked",this.wholeWordOption.checked),o.setCssClass(this.caseSensitiveOption,"checked",this.caseSensitiveOption.checked);var t=this.editor.getReadOnly();this.replaceOption.style.display=t?"none":"",this.replaceBox.style.display=this.replaceOption.checked&&!t?"":"none",this.find(!1,!1,e)},i.prototype.highlight=function(e){this.editor.session.highlight(e||this.editor.$search.$options.re),this.editor.renderer.updateBackMarkers()},i.prototype.find=function(e,t,n){e=!this.editor.find(this.searchInput.value,{skipCurrent:e,backwards:t,wrap:!0,regExp:this.regExpOption.checked,caseSensitive:this.caseSensitiveOption.checked,wholeWord:this.wholeWordOption.checked,preventScroll:n,range:this.searchRange})&&this.searchInput.value,o.setCssClass(this.searchBox,"ace_nomatch",e),this.editor._emit("findSearchBox",{match:!e}),this.highlight(),this.updateCounter()},i.prototype.updateCounter=function(){var e=this.editor,t=e.$search.$options.re,n=t.unicode,i=0,o=0;if(t){var a,s,l=this.searchRange?e.session.getTextRange(this.searchRange):e.getValue(),c=e.session.doc.positionToIndex(e.selection.anchor);for(this.searchRange&&(c-=e.session.doc.positionToIndex(this.searchRange.start)),t.lastIndex=0;(s=t.exec(l))&&((a=s.index)<=c&&o++,!(999<++i))&&(s[0]||(t.lastIndex=a+=r.skipEmptyMatch(l,a,n),!(a>=l.length))););}this.searchCounter.textContent=d("$0 of $1",[o,999%\\^`{|}]|%[0-9a-f]{2})|\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?)*\})*$/i,p=/^(?:(?:http[s\u017F]?|ftp):\/\/)(?:(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+(?::(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*)?@)?(?:(?!10(?:\.[0-9]{1,3}){3})(?!127(?:\.[0-9]{1,3}){3})(?!169\.254(?:\.[0-9]{1,3}){2})(?!192\.168(?:\.[0-9]{1,3}){2})(?!172\.(?:1[6-9]|2[0-9]|3[01])(?:\.[0-9]{1,3}){2})(?:[1-9][0-9]?|1[0-9][0-9]|2[01][0-9]|22[0-3])(?:\.(?:1?[0-9]{1,2}|2[0-4][0-9]|25[0-5])){2}(?:\.(?:[1-9][0-9]?|1[0-9][0-9]|2[0-4][0-9]|25[0-4]))|(?:(?:(?:[0-9a-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+-)*(?:[0-9a-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)(?:\.(?:(?:[0-9a-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+-)*(?:[0-9a-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)*(?:\.(?:(?:[a-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){2,})))(?::[0-9]{2,5})?(?:\/(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*)?$/i,f=/^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i,g=/^(?:\/(?:[^~/]|~0|~1)*)*$/,m=/^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i,y=/^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/;(e.exports=i).fast={date:/^\d\d\d\d-[0-1]\d-[0-3]\d$/,time:/^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)?$/i,"date-time":/^\d\d\d\d-[0-1]\d-[0-3]\d[t\s](?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i,uri:/^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/)?[^\s]*$/i,"uri-reference":/^(?:(?:[a-z][a-z0-9+\-.]*:)?\/?\/)?(?:[^\\\s#][^\s#]*)?(?:#[^\\\s]*)?$/i,"uri-template":h,url:p,email:/^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i,hostname:n,ipv4:/^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/,ipv6:/^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i,regex:a,uuid:f,"json-pointer":g,"json-pointer-uri-fragment":m,"relative-json-pointer":y},i.full={date:o,time:r,"date-time":function(e){return e=e.split(v),2==e.length&&o(e[0])&&r(e[1],!0)},uri:function(e){return b.test(e)&&u.test(e)},"uri-reference":/^(?:[a-z][a-z0-9+\-.]*:)?(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'"()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\?(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i,"uri-template":h,url:p,email:/^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i,hostname:n,ipv4:/^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/,ipv6:/^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i,regex:a,uuid:f,"json-pointer":g,"json-pointer-uri-fragment":m,"relative-json-pointer":y};var v=/t|\s/i,b=/\/|:/,C=/[^\\]\\Z/},2620:function(e,t,n){"use strict";function i(e,t,n,v){function b(){var e=D.validate,t=e.apply(this,arguments);return b.errors=e.errors,t}function C(e,n,o,p){var v=!n||n.schema==e;if(n.schema!=t.schema)return i.call(S,e,n,o,p);o=!0===e.$async,p=f({isTop:!0,schema:e,isRoot:v,baseId:p,root:n,schemaPath:"",errSchemaPath:"#",errorPath:'""',MissingRefError:h.MissingRef,RULES:F,validate:f,util:u,resolve:d,resolveRef:x,usePattern:A,useDefault:k,useCustomRule:_,opts:T,formats:B,logger:S.logger,self:S}),p=c(E,s)+c($,r)+c(j,a)+c(N,l)+p,T.processCode&&(p=T.processCode(p,e));try{var b=new Function("self","RULES","formats","root","refVal","defaults","customRules","equal","ucs2length","ValidationError",p)(S,F,B,t,E,j,N,m,g,y);E[0]=b}catch(e){throw S.logger.error("Error compiling schema, function code:",p),e}return b.schema=e,b.errors=null,b.refs=R,b.refVal=E,b.root=v?b:n,o&&(b.$async=!0),!0===T.sourceCode&&(b.source={code:p,patterns:$,defaults:j}),b}function x(e,o,r){o=d.url(e,o);var a=R[o];if(void 0!==a)return w(s=E[a],l="refVal["+a+"]");if(!r&&t.refs&&void 0!==(a=t.refs[o]))return w(s=t.refVal[a],l=I(o,s));var s,l=I(o),r=d.call(S,C,t,o);if(void 0!==(r=void 0===r&&(a=n&&n[o])?d.inlineRef(a,T.inlineRefs)?a:i.call(S,a,t,n,e):r))return s=r,a=R[a=o],E[a]=s,w(r,l);delete R[o]}function I(e,t){var n=E.length;return E[n]=t,"refVal"+(R[e]=n)}function w(e,t){return"object"==(void 0===e?"undefined":_typeof(e))||"boolean"==typeof e?{code:t,schema:e,inline:!0}:{code:t,$async:e&&!!e.$async}}function A(e){var t=L[e];return void 0===t&&(t=L[e]=$.length,$[t]=e),"pattern"+t}function k(e){switch(void 0===e?"undefined":_typeof(e)){case"boolean":case"number":return""+e;case"string":return u.toQuotedString(e);case"object":var t,n;return null===e?"null":(t=p(e),void 0===(n=O[t])&&(n=O[t]=j.length,j[n]=e),"default"+n)}}function _(e,t,n,i){if(!1!==S._opts.validateSchema){var o=e.definition.dependencies;if(o&&!o.every(function(e){return Object.prototype.hasOwnProperty.call(n,e)}))throw new Error("parent schema must have all required keywords: "+o.join(","));if((o=e.definition.validateSchema)&&!o(t)){if(o="keyword schema is invalid: "+S.errorsText(o.errors),"log"!=S._opts.validateSchema)throw new Error(o);S.logger.error(o)}}var r,o=e.definition.compile,a=e.definition.inline,s=e.definition.macro;if(o)r=o.call(S,t,n,i);else if(s)r=s.call(S,t,n,i),!1!==T.validateSchema&&S.validateSchema(r,!0);else if(a)r=a.call(S,i,e.keyword,t,n);else if(!(r=e.definition.validate))return;if(void 0===r)throw new Error('custom keyword "'+e.keyword+'"failed to compile');return o=N.length,{code:"customRule"+o,validate:N[o]=r}}var S=this,T=this._opts,E=[void 0],R={},$=[],L={},j=[],O={},N=[],M=(t=t||{schema:e,refVal:E,refs:R},function(e,t,n){var i=o.call(this,e,t,n);return 0<=i?{index:i,compiling:!0}:(i=this._compilations.length,this._compilations[i]={schema:e,root:t,baseId:n},{index:i,compiling:!1})}.call(this,e,t,v)),D=this._compilations[M.index];if(M.compiling)return D.callValidate=b;var B=this._formats,F=this.RULES;try{var P=C(e,t,n,v),H=(D.validate=P,D.callValidate);return H&&(H.schema=P.schema,H.errors=null,H.refs=P.refs,H.refVal=P.refVal,H.root=P.root,H.$async=P.$async,T.sourceCode)&&(H.source=P.source),P}finally{(function(e,t,n){0<=(e=o.call(this,e,t,n))&&this._compilations.splice(e,1)}).call(this,e,t,v)}}function o(e,t,n){for(var i=0;i",w=v?">":"<",A=void 0;if(!m&&"number"!=typeof u&&void 0!==u)throw new Error(t+" must be number");if(!x&&void 0!==C&&"number"!=typeof C&&"boolean"!=typeof C)throw new Error(b+" must be number or boolean");x?(d=e.util.getData(C.$data,d,e.dataPathArr),r="exclIsNumber"+c,a="' + "+(s="op"+c)+" + '",A=b,(k=k||[]).push(l=l+" var schemaExcl"+c+" = "+d+"; var "+(i="exclusive"+c)+"; var "+(o="exclType"+c)+" = typeof "+(d="schemaExcl"+c)+"; if ("+o+" != 'boolean' && "+o+" != 'undefined' && "+o+" != 'number') { "),l="",!1!==e.createErrors?(l+=" { keyword: '"+(A||"_exclusiveLimit")+"' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(p)+" , params: {} ",!1!==e.opts.messages&&(l+=" , message: '"+b+" should be boolean' "),e.opts.verbose&&(l+=" , schema: validate.schema"+h+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+g+" "),l+=" } "):l+=" {} ",_=l,l=k.pop(),!e.compositeRule&&f?e.async?l+=" throw new ValidationError(["+_+"]); ":l+=" validate.errors = ["+_+"]; return false; ":l+=" var err = "+_+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",l+=" } else if ( ",m&&(l+=" ("+y+" !== undefined && typeof "+y+" != 'number') || "),l+=" "+o+" == 'number' ? ( ("+i+" = "+y+" === undefined || "+d+" "+I+"= "+y+") ? "+g+" "+w+"= "+d+" : "+g+" "+w+" "+y+" ) : ( ("+i+" = "+d+" === true) ? "+g+" "+w+"= "+y+" : "+g+" "+w+" "+y+" ) || "+g+" !== "+g+") { var op"+c+" = "+i+" ? '"+I+"' : '"+I+"='; ",void 0===u&&(p=e.errSchemaPath+"/"+(A=b),y=d,m=x)):(a=I,(r="number"==typeof C)&&m?(s="'"+a+"'",l+=" if ( ",m&&(l+=" ("+y+" !== undefined && typeof "+y+" != 'number') || "),l+=" ( "+y+" === undefined || "+C+" "+I+"= "+y+" ? "+g+" "+w+"= "+C+" : "+g+" "+w+" "+y+" ) || "+g+" !== "+g+") { "):(r&&void 0===u?(i=!0,p=e.errSchemaPath+"/"+(A=b),y=C,w+="="):(r&&(y=Math[v?"min":"max"](C,u)),C===(!r||y)?(i=!0,p=e.errSchemaPath+"/"+(A=b),w+="="):(i=!1,a+="=")),s="'"+a+"'",l+=" if ( ",m&&(l+=" ("+y+" !== undefined && typeof "+y+" != 'number') || "),l+=" "+g+" "+w+" "+y+" || "+g+" !== "+g+") { ")),A=A||t,(k=k||[]).push(l),l="",!1!==e.createErrors?(l+=" { keyword: '"+(A||"_limit")+"' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(p)+" , params: { comparison: "+s+", limit: "+y+", exclusive: "+i+" } ",!1!==e.opts.messages&&(l=l+" , message: 'should be "+a+" "+(m?"' + "+y:y+"'")),e.opts.verbose&&(l=(l+=" , schema: ")+(m?"validate.schema"+h:""+u)+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+g+" "),l+=" } "):l+=" {} ";var k,_=l;return l=k.pop(),!e.compositeRule&&f?e.async?l+=" throw new ValidationError(["+_+"]); ":l+=" validate.errors = ["+_+"]; return false; ":l+=" var err = "+_+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",l+=" } ",f&&(l+=" else { "),l}},9337:function(e){"use strict";e.exports=function(e,t,n){var i,o=" ",r=e.level,a=e.dataLevel,s=e.schema[t],l=e.schemaPath+e.util.getProperty(t),c=e.errSchemaPath+"/"+t,d=!e.opts.allErrors,u="data"+(a||""),h=e.opts.$data&&s&&s.$data,a=h?(o+=" var schema"+r+" = "+e.util.getData(s.$data,a,e.dataPathArr)+"; ","schema"+r):s;if(h||"number"==typeof s)return o+="if ( ",h&&(o+=" ("+a+" !== undefined && typeof "+a+" != 'number') || "),(r=[]).push(o+=" "+u+".length "+("maxItems"==(i=t)?">":"<")+" "+a+") { "),o="",!1!==e.createErrors?(o+=" { keyword: '"+(i||"_limitItems")+"' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(c)+" , params: { limit: "+a+" } ",!1!==e.opts.messages&&(o=(o=(o+=" , message: 'should NOT have ")+("maxItems"==t?"more":"fewer")+" than ")+(h?"' + "+a+" + '":""+s)+" items' "),e.opts.verbose&&(o=(o+=" , schema: ")+(h?"validate.schema"+l:""+s)+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+u+" "),o+=" } "):o+=" {} ",i=o,o=r.pop(),!e.compositeRule&&d?e.async?o+=" throw new ValidationError(["+i+"]); ":o+=" validate.errors = ["+i+"]; return false; ":o+=" var err = "+i+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",o+="} ",d&&(o+=" else { "),o;throw new Error(t+" must be number")}},5433:function(e){"use strict";e.exports=function(e,t,n){var i,o=" ",r=e.level,a=e.dataLevel,s=e.schema[t],l=e.schemaPath+e.util.getProperty(t),c=e.errSchemaPath+"/"+t,d=!e.opts.allErrors,u="data"+(a||""),h=e.opts.$data&&s&&s.$data,a=h?(o+=" var schema"+r+" = "+e.util.getData(s.$data,a,e.dataPathArr)+"; ","schema"+r):s;if(h||"number"==typeof s)return o+="if ( ",h&&(o+=" ("+a+" !== undefined && typeof "+a+" != 'number') || "),!1===e.opts.unicode?o+=" "+u+".length ":o+=" ucs2length("+u+") ",(r=[]).push(o+=" "+("maxLength"==(i=t)?">":"<")+" "+a+") { "),o="",!1!==e.createErrors?(o+=" { keyword: '"+(i||"_limitLength")+"' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(c)+" , params: { limit: "+a+" } ",!1!==e.opts.messages&&(o=(o=(o+=" , message: 'should NOT be ")+("maxLength"==t?"longer":"shorter")+" than ")+(h?"' + "+a+" + '":""+s)+" characters' "),e.opts.verbose&&(o=(o+=" , schema: ")+(h?"validate.schema"+l:""+s)+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+u+" "),o+=" } "):o+=" {} ",i=o,o=r.pop(),!e.compositeRule&&d?e.async?o+=" throw new ValidationError(["+i+"]); ":o+=" validate.errors = ["+i+"]; return false; ":o+=" var err = "+i+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",o+="} ",d&&(o+=" else { "),o;throw new Error(t+" must be number")}},7724:function(e){"use strict";e.exports=function(e,t,n){var i,o=" ",r=e.level,a=e.dataLevel,s=e.schema[t],l=e.schemaPath+e.util.getProperty(t),c=e.errSchemaPath+"/"+t,d=!e.opts.allErrors,u="data"+(a||""),h=e.opts.$data&&s&&s.$data,a=h?(o+=" var schema"+r+" = "+e.util.getData(s.$data,a,e.dataPathArr)+"; ","schema"+r):s;if(h||"number"==typeof s)return o+="if ( ",h&&(o+=" ("+a+" !== undefined && typeof "+a+" != 'number') || "),(r=[]).push(o+=" Object.keys("+u+").length "+("maxProperties"==(i=t)?">":"<")+" "+a+") { "),o="",!1!==e.createErrors?(o+=" { keyword: '"+(i||"_limitProperties")+"' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(c)+" , params: { limit: "+a+" } ",!1!==e.opts.messages&&(o=(o=(o+=" , message: 'should NOT have ")+("maxProperties"==t?"more":"fewer")+" than ")+(h?"' + "+a+" + '":""+s)+" properties' "),e.opts.verbose&&(o=(o+=" , schema: ")+(h?"validate.schema"+l:""+s)+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+u+" "),o+=" } "):o+=" {} ",i=o,o=r.pop(),!e.compositeRule&&d?e.async?o+=" throw new ValidationError(["+i+"]); ":o+=" validate.errors = ["+i+"]; return false; ":o+=" var err = "+i+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",o+="} ",d&&(o+=" else { "),o;throw new Error(t+" must be number")}},3531:function(e){"use strict";e.exports=function(e,t,n){var i=" ",o=e.schema[t],r=e.schemaPath+e.util.getProperty(t),a=e.errSchemaPath+"/"+t,s=!e.opts.allErrors,l=e.util.copy(e),c="",d="valid"+ ++l.level,u=l.baseId,h=!0,p=o;if(p)for(var f,g=-1,m=p.length-1;g "+x+") { ",w=d+"["+x+"]",u.schema=k,u.schemaPath=s+"["+x+"]",u.errSchemaPath=l+"/"+x,u.errorPath=e.util.getPathExpr(e.errorPath,x,e.opts.jsonPointers,!0),u.dataPathArr[f]=x,A=e.validate(u),u.baseId=m,e.util.varOccurences(A,g)<2?i+=" "+e.util.varReplace(A,g,w)+" ":i+=" var "+g+" = "+w+"; "+A+" ",i+=" } ",c)&&(i+=" if ("+p+") { ",h+="}")}"object"==(void 0===b?"undefined":_typeof(b))&&(e.opts.strictKeywords?"object"==(void 0===b?"undefined":_typeof(b))&&0 "+a.length+") { for (var "+o+" = "+a.length+"; "+o+" < "+d+".length; "+o+"++) { ",u.errorPath=e.util.getPathExpr(e.errorPath,o,e.opts.jsonPointers,!0),w=d+"["+o+"]",u.dataPathArr[f]=o,A=e.validate(u),u.baseId=m,e.util.varOccurences(A,g)<2?i+=" "+e.util.varReplace(A,g,w)+" ":i+=" var "+g+" = "+w+"; "+A+" ",c&&(i+=" if (!"+p+") break; "),i+=" } } ",c)&&(i+=" if ("+p+") { ",h+="}")}else(e.opts.strictKeywords?"object"==(void 0===a?"undefined":_typeof(a))&&0 1e-"+e.opts.multipleOfPrecision+" ":i+=" division"+o+" !== parseInt(division"+o+") ",i+=" ) ",u&&(i+=" ) "),t=[],t.push(i+=" ) { "),i="",!1!==e.createErrors?(i+=" { keyword: 'multipleOf' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(l)+" , params: { multipleOf: "+r+" } ",!1!==e.opts.messages&&(i=i+" , message: 'should be multiple of "+(u?"' + "+r:r+"'")),e.opts.verbose&&(i=(i+=" , schema: ")+(u?"validate.schema"+s:""+a)+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+d+" "),i+=" } "):i+=" {} ",o=i,i=t.pop(),!e.compositeRule&&c?e.async?i+=" throw new ValidationError(["+o+"]); ":i+=" validate.errors = ["+o+"]; return false; ":i+=" var err = "+o+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",i+="} ",c&&(i+=" else { "),i}},1004:function(e){"use strict";e.exports=function(e,t,n){var i,o,r=" ",a=e.level,s=e.dataLevel,l=e.schema[t],c=e.schemaPath+e.util.getProperty(t),t=e.errSchemaPath+"/"+t,d=!e.opts.allErrors,s="data"+(s||""),a="errs__"+a,u=e.util.copy(e),h="valid"+ ++u.level;return(e.opts.strictKeywords?"object"==(void 0===l?"undefined":_typeof(l))&&0=e.opts.loopRequired,b=e.opts.ownProperties;if(t){if(i+=" var missing"+o+"; ",a){u||(i+=" var "+r+" = validate.schema"+s+"; ");var C="' + "+(_="schema"+o+"["+(w="i"+o)+"]")+" + '";e.opts._errorDataPathProperty&&(e.errorPath=e.util.getPathExpr(v,_,e.opts.jsonPointers)),i+=" var "+d+" = true; ",u&&(i+=" if (schema"+o+" === undefined) "+d+" = true; else if (!Array.isArray(schema"+o+")) "+d+" = false; else {"),i+=" for (var "+w+" = 0; "+w+" < "+r+".length; "+w+"++) { "+d+" = "+c+"["+r+"["+w+"]] !== undefined ",b&&(i+=" && Object.prototype.hasOwnProperty.call("+c+", "+r+"["+w+"]) "),i+="; if (!"+d+") break; } ",u&&(i+=" } "),(k=k||[]).push(i+=" if (!"+d+") { "),i="",!1!==e.createErrors?(i+=" { keyword: 'required' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(l)+" , params: { missingProperty: '"+C+"' } ",!1!==e.opts.messages&&(i+=" , message: '",e.opts._errorDataPathProperty?i+="is a required property":i+="should have required property \\'"+C+"\\'",i+="' "),e.opts.verbose&&(i+=" , schema: validate.schema"+s+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+c+" "),i+=" } "):i+=" {} ";var x=i,i=k.pop()}else{i+=" if ( ";var I=h;if(I)for(var w=-1,A=I.length-1;w 1) { ",r=e.schema.items&&e.schema.items.type,a=Array.isArray(r),!r||"object"==r||"array"==r||a&&(0<=r.indexOf("object")||0<=r.indexOf("array"))?o+=" outer: for (;i--;) { for (j = i; j--;) { if (equal("+d+"[i], "+d+"[j])) { "+u+" = false; break outer; } } } ":(o=(o+=" var itemIndices = {}, item; for (;i--;) { var item = "+d+"[i]; ")+" if ("+e.util["checkDataType"+(a?"s":"")](r,"item",e.opts.strictNumbers,!0)+") continue; ",a&&(o+=" if (typeof item == 'string') item = '\"' + item; "),o+=" if (typeof itemIndices[item] == 'number') { "+u+" = false; j = itemIndices[item]; break; } itemIndices[item] = i; } "),o+=" } ",h&&(o+=" } "),(i=i||[]).push(o+=" if (!"+u+") { "),o="",!1!==e.createErrors?(o+=" { keyword: 'uniqueItems' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(t)+" , params: { i: i, j: j } ",!1!==e.opts.messages&&(o+=" , message: 'should NOT have duplicate items (items ## ' + j + ' and ' + i + ' are identical)' "),e.opts.verbose&&(o=(o+=" , schema: ")+(h?"validate.schema"+l:""+s)+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+d+" "),o+=" } "):o+=" {} ",r=o,o=i.pop(),!e.compositeRule&&c?e.async?o+=" throw new ValidationError(["+r+"]); ":o+=" validate.errors = ["+r+"]; return false; ":o+=" var err = "+r+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",o+=" } ",c&&(o+=" else { ")):c&&(o+=" if (true) { "),o}},1035:function(e){"use strict";e.exports=function(e,t,n){function i(e){for(var t=e.rules,n=0;n",9:"Array"},h="UnquotedIdentifier",p="QuotedIdentifier",f="Rbracket",g="Rparen",m="Comma",y="Colon",v="Rbrace",b="Number",C="Current",x="Expref",I="Pipe",w="Flatten",A="Star",k="Filter",_="Lbrace",S="Lbracket",T="Lparen",E="Literal",R={".":"Dot","*":A,",":m,":":y,"{":_,"}":v,"]":f,"(":T,")":g,"@":C},$={"<":!0,">":!0,"=":!0,"!":!0},L={" ":!0,"\t":!0,"\n":!0};a.prototype={tokenize:function(e){var t,n,i=[];for(this._current=0;this._current"===n?"="===e[this._current]?(this._current++,{type:"GTE",value:">=",start:t}):{type:"GT",value:">",start:t}:"="===n&&"="===e[this._current]?(this._current++,{type:"EQ",value:"==",start:t}):void 0},_consumeLiteral:function(e){this._current++;for(var t=this._current,n=e.length;"`"!==e[this._current]&&this._currentNumber.MAX_SAFE_INTEGER||i=e.length)throw new SyntaxError("Unexpected end of JSON input")}var b={},C=0,x=0,I=0,w=n&&n.bigint&&"undefined"!=typeof BigInt;return{data:i("",!0),pointers:b}},t.stringify=function(e,t,r){function a(e){b+=e.length,C+=e.length,m+=e}function s(e){if(h){for(m+="\n"+c(e,h),v++,b=0;e--;)d?(v+=d,b=f):b+=f,C+=p;C+=1}}function l(e,t){y[e]=y[e]||{},y[e][t]={line:v,column:b,pos:C}}function c(e,t){return Array(e+1).join(t)}if(n(e)){var d=0;switch(_typeof(h="object"==(void 0===r?"undefined":_typeof(r))?r.space:r)){case"number":var u=101){t[0]=t[0].slice(0,-1);for(var i=t.length-1,o=1;o1&&(i=n[0]+"@",e=n[1]),e=e.replace(B,"."),i+c(e.split("."),t).join(".")}function u(e){for(var t=[],n=0,i=e.length;n=55296&&o<=56319&&n>6|192).toString(16).toUpperCase()+"%"+(63&t|128).toString(16).toUpperCase():"%"+(t>>12|224).toString(16).toUpperCase()+"%"+(t>>6&63|128).toString(16).toUpperCase()+"%"+(63&t|128).toString(16).toUpperCase()}function p(e){for(var t="",n=0,i=e.length;n=194&&o<224){if(i-n>=6){var r=parseInt(e.substr(n+4,2),16);t+=String.fromCharCode((31&o)<<6|63&r)}else t+=e.substr(n,6);n+=6}else if(o>=224){if(i-n>=9){var a=parseInt(e.substr(n+4,2),16),s=parseInt(e.substr(n+7,2),16);t+=String.fromCharCode((15&o)<<12|(63&a)<<6|63&s)}else t+=e.substr(n,9);n+=9}else t+=e.substr(n,3),n+=3}return t}function f(e,t){function n(e){var n=p(e);return n.match(t.UNRESERVED)?n:e}return e.scheme&&(e.scheme=String(e.scheme).replace(t.PCT_ENCODED,n).toLowerCase().replace(t.NOT_SCHEME,"")),void 0!==e.userinfo&&(e.userinfo=String(e.userinfo).replace(t.PCT_ENCODED,n).replace(t.NOT_USERINFO,h).replace(t.PCT_ENCODED,o)),void 0!==e.host&&(e.host=String(e.host).replace(t.PCT_ENCODED,n).toLowerCase().replace(t.NOT_HOST,h).replace(t.PCT_ENCODED,o)),void 0!==e.path&&(e.path=String(e.path).replace(t.PCT_ENCODED,n).replace(e.scheme?t.NOT_PATH:t.NOT_PATH_NOSCHEME,h).replace(t.PCT_ENCODED,o)),void 0!==e.query&&(e.query=String(e.query).replace(t.PCT_ENCODED,n).replace(t.NOT_QUERY,h).replace(t.PCT_ENCODED,o)),void 0!==e.fragment&&(e.fragment=String(e.fragment).replace(t.PCT_ENCODED,n).replace(t.NOT_FRAGMENT,h).replace(t.PCT_ENCODED,o)),e}function g(e){return e.replace(/^0*(.*)/,"$1")||"0"}function m(e,t){var n=e.match(t.IPV4ADDRESS)||[],i=L(n,2),o=i[1];return o?o.split(".").map(g).join("."):e}function y(e,t){var n=e.match(t.IPV6ADDRESS)||[],i=L(n,3),o=i[1],r=i[2];if(o){for(var a=o.toLowerCase().split("::").reverse(),s=L(a,2),l=s[0],c=s[1],d=c?c.split(":").map(g):[],u=l.split(":").map(g),h=t.IPV4ADDRESS.test(u[u.length-1]),p=h?7:8,f=u.length-p,y=Array(p),v=0;v1){var I=y.slice(0,C.index),w=y.slice(C.index+C.length);x=I.join(":")+"::"+w.join(":")}else x=y.join(":");return r&&(x+="%"+r),x}return e}function v(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n={},i=!1!==t.iri?$:R;"suffix"===t.reference&&(e=(t.scheme?t.scheme+":":"")+"//"+e);var o=e.match(Y);if(o){U?(n.scheme=o[1],n.userinfo=o[3],n.host=o[4],n.port=parseInt(o[5],10),n.path=o[6]||"",n.query=o[7],n.fragment=o[8],isNaN(n.port)&&(n.port=o[5])):(n.scheme=o[1]||void 0,n.userinfo=-1!==e.indexOf("@")?o[3]:void 0,n.host=-1!==e.indexOf("//")?o[4]:void 0,n.port=parseInt(o[5],10),n.path=o[6]||"",n.query=-1!==e.indexOf("?")?o[7]:void 0,n.fragment=-1!==e.indexOf("#")?o[8]:void 0,isNaN(n.port)&&(n.port=e.match(/\/\/(?:.|\n)*\:(?:\/|\?|\#|$)/)?o[4]:void 0)),n.host&&(n.host=y(m(n.host,i),i)),void 0!==n.scheme||void 0!==n.userinfo||void 0!==n.host||void 0!==n.port||n.path||void 0!==n.query?void 0===n.scheme?n.reference="relative":void 0===n.fragment?n.reference="absolute":n.reference="uri":n.reference="same-document",t.reference&&"suffix"!==t.reference&&t.reference!==n.reference&&(n.error=n.error||"URI is not a "+t.reference+" reference.");var r=X[(t.scheme||n.scheme||"").toLowerCase()];if(t.unicodeSupport||r&&r.unicodeSupport)f(n,i);else{if(n.host&&(t.domainHost||r&&r.domainHost))try{n.host=K.toASCII(n.host.replace(i.PCT_ENCODED,p).toLowerCase())}catch(e){n.error=n.error||"Host's domain name can not be converted to ASCII via punycode: "+e}f(n,R)}r&&r.parse&&r.parse(n,t)}else n.error=n.error||"URI can not be parsed.";return n}function b(e,t){var n=!1!==t.iri?$:R,i=[];return void 0!==e.userinfo&&(i.push(e.userinfo),i.push("@")),void 0!==e.host&&i.push(y(m(String(e.host),n),n).replace(n.IPV6ADDRESS,function(e,t,n){return"["+t+(n?"%25"+n:"")+"]"})),"number"!=typeof e.port&&"string"!=typeof e.port||(i.push(":"),i.push(String(e.port))),i.length?i.join(""):void 0}function C(e){for(var t=[];e.length;)if(e.match(J))e=e.replace(J,"");else if(e.match(q))e=e.replace(q,"/");else if(e.match(Q))e=e.replace(Q,"/"),t.pop();else if("."===e||".."===e)e="";else{var n=e.match(ee);if(!n)throw new Error("Unexpected dot segment condition");var i=n[0];e=e.slice(i.length),t.push(i)}return t.join("")}function x(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.iri?$:R,i=[],o=X[(t.scheme||e.scheme||"").toLowerCase()];if(o&&o.serialize&&o.serialize(e,t),e.host)if(n.IPV6ADDRESS.test(e.host));else if(t.domainHost||o&&o.domainHost)try{e.host=t.iri?K.toUnicode(e.host):K.toASCII(e.host.replace(n.PCT_ENCODED,p).toLowerCase())}catch(n){e.error=e.error||"Host's domain name can not be converted to "+(t.iri?"Unicode":"ASCII")+" via punycode: "+n}f(e,n),"suffix"!==t.reference&&e.scheme&&(i.push(e.scheme),i.push(":"));var r=b(e,t);if(void 0!==r&&("suffix"!==t.reference&&i.push("//"),i.push(r),e.path&&"/"!==e.path.charAt(0)&&i.push("/")),void 0!==e.path){var a=e.path;t.absolutePath||o&&o.absolutePath||(a=C(a)),void 0===r&&(a=a.replace(/^\/\//,"/%2F")),i.push(a)}return void 0!==e.query&&(i.push("?"),i.push(e.query)),void 0!==e.fragment&&(i.push("#"),i.push(e.fragment)),i.join("")}function I(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},i=arguments[3],o={};return i||(e=v(x(e,n),n),t=v(x(t,n),n)),n=n||{},!n.tolerant&&t.scheme?(o.scheme=t.scheme,o.userinfo=t.userinfo,o.host=t.host,o.port=t.port,o.path=C(t.path||""),o.query=t.query):(void 0!==t.userinfo||void 0!==t.host||void 0!==t.port?(o.userinfo=t.userinfo,o.host=t.host,o.port=t.port,o.path=C(t.path||""),o.query=t.query):(t.path?("/"===t.path.charAt(0)?o.path=C(t.path):(void 0===e.userinfo&&void 0===e.host&&void 0===e.port||e.path?e.path?o.path=e.path.slice(0,e.path.lastIndexOf("/")+1)+t.path:o.path=t.path:o.path="/"+t.path,o.path=C(o.path)),o.query=t.query):(o.path=e.path,void 0!==t.query?o.query=t.query:o.query=e.query),o.userinfo=e.userinfo,o.host=e.host,o.port=e.port),o.scheme=e.scheme),o.fragment=t.fragment,o}function w(e,t,n){var i=a({scheme:"null"},n);return x(I(v(e,i),v(t,i),i,!0),i)}function A(e,t){return"string"==typeof e?e=x(v(e,t),t):"object"===i(e)&&(e=v(x(e,t),t)),e}function k(e,t,n){return"string"==typeof e?e=x(v(e,n),n):"object"===i(e)&&(e=x(e,n)),"string"==typeof t?t=x(v(t,n),n):"object"===i(t)&&(t=x(t,n)),e===t}function _(e,t){return e&&e.toString().replace(t&&t.iri?$.ESCAPE:R.ESCAPE,h)}function S(e,t){return e&&e.toString().replace(t&&t.iri?$.PCT_ENCODED:R.PCT_ENCODED,p)}function T(e){return"boolean"==typeof e.secure?e.secure:"wss"===String(e.scheme).toLowerCase()}function E(e){var t=p(e);return t.match(de)?t:e}var R=s(!1),$=s(!0),L=function(){function e(e,t){var n=[],i=!0,o=!1,r=void 0;try{for(var a,s=e[Symbol.iterator]();!(i=(a=s.next()).done)&&(n.push(a.value),!t||n.length!==t);i=!0);}catch(e){o=!0,r=e}finally{try{!i&&s.return&&s.return()}finally{if(o)throw r}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),j=function(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t= 0x80 (not a basic code point)","invalid-input":"Invalid input"},P=Math.floor,H=String.fromCharCode,W=function(e){return e-48<10?e-22:e-65<26?e-65:e-97<26?e-97:N},V=function(e,t){return e+22+75*(e<26)-((0!=t)<<5)},G=function(e,t,n){var i=0;for(e=n?P(e/700):e>>1,e+=P(e/t);e>455;i+=N)e=P(e/35);return P(i+36*e/(e+38))},Z=function(e){var t=[],n=e.length,i=0,o=128,r=72,a=e.lastIndexOf("-");a<0&&(a=0);for(var s=0;s=128&&l("not-basic"),t.push(e.charCodeAt(s));for(var c=a>0?a+1:0;c=n&&l("invalid-input");var p=W(e.charCodeAt(c++));(p>=N||p>P((O-i)/u))&&l("overflow"),i+=p*u;var f=h<=r?1:h>=r+26?26:h-r;if(pP(O/g)&&l("overflow"),u*=g}var m=t.length+1;r=G(i-d,m,0==d),P(i/m)>O-o&&l("overflow"),o+=P(i/m),i%=m,t.splice(i++,0,o)}return String.fromCodePoint.apply(String,t)},z=function(e){var t=[];e=u(e);var n=e.length,i=128,o=0,r=72,a=!0,s=!1,c=void 0;try{for(var d,h=e[Symbol.iterator]();!(a=(d=h.next()).done);a=!0){var p=d.value;p<128&&t.push(H(p))}}catch(e){s=!0,c=e}finally{try{!a&&h.return&&h.return()}finally{if(s)throw c}}var f=t.length,g=f;for(f&&t.push("-");g=i&&IP((O-o)/w)&&l("overflow"),o+=(m-i)*w,i=m;var A=!0,k=!1,_=void 0;try{for(var S,T=e[Symbol.iterator]();!(A=(S=T.next()).done);A=!0){var E=S.value;if(EO&&l("overflow"),E==i){for(var R=o,$=N;;$+=N){var L=$<=r?1:$>=r+26?26:$-r;if(RA-Z\\x5E-\\x7E]",'[\\"\\\\]'),de=new RegExp(ae,"g"),ue=new RegExp(le,"g"),he=new RegExp(t("[^]","[A-Za-z0-9\\!\\$\\%\\'\\*\\+\\-\\^\\_\\`\\{\\|\\}\\~]","[\\.]",'[\\"]',ce),"g"),pe=new RegExp(t("[^]",ae,"[\\!\\$\\'\\(\\)\\*\\+\\,\\;\\:\\@]"),"g"),fe=pe,ge={scheme:"mailto",parse:function(e,t){var n=e,i=n.to=n.path?n.path.split(","):[];if(n.path=void 0,n.query){for(var o=!1,r={},a=n.query.split("&"),s=0,l=a.length;s1&&void 0!==arguments[1]?arguments[1]:1;return(t>0?e.toFixed(t).replace(/0+$/,"").replace(/\.$/,""):e.toString())||"0"}function t(e){var t=document.createElement("div");return t.innerHTML=e,t.firstElementChild}function n(e,t,n){function i(e,t,n){return Math.max(t,Math.min(e,n))}function o(e,o,r){if(r&&(s=!0),s){e.preventDefault();var a=t.getBoundingClientRect(),l=a.width,c=a.height,d=o.clientX,u=o.clientY,h=i(d-a.left,0,l),p=i(u-a.top,0,c);n(h/l,p/c)}}function r(e,t){1===(void 0===e.buttons?e.which:e.buttons)?o(e,e,t):s=!1}function a(e,t){1===e.touches.length?o(e,e.touches[0],t):s=!1}var s=!1;e.add(t,"mousedown",function(e){r(e,!0)}),e.add(t,"touchstart",function(e){a(e,!0)}),e.add(window,"mousemove",r),e.add(t,"touchmove",a),e.add(window,"mouseup",function(e){s=!1}),e.add(t,"touchend",function(e){s=!1}),e.add(t,"touchcancel",function(e){s=!1})}function i(e,t){return(t||document).querySelector(e)}function o(e){e.preventDefault(),e.stopPropagation()}function r(e,t,n,i,r){e.add(t,h,function(e){n.indexOf(e.key)>=0&&(r&&o(e),i(e))})}var a=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},s=function(){function e(e,t){for(var n=0;n.5?h/(2-a-s):h/(a+s),a){case n:c=(i-o)/h+(i1&&(n-=1),n<1/6?e+6*(t-e)*n:n<.5?t:n<2/3?e+(t-e)*(2/3-n)*6:e},u=o<.5?o*(1+i):o+i-o*i,h=2*o-u;a=d(h,u,n+1/3),s=d(h,u,n),c=d(h,u,n-1/3)}var p=[255*a,255*s,255*c].map(Math.round);return p[3]=r,p}}]),t}(),u=function(){function e(){a(this,e),this._events=[]}return s(e,[{key:"add",value:function(e,t,n){e.addEventListener(t,n,!1),this._events.push({target:e,type:t,handler:n})}},{key:"remove",value:function(t,n,i){this._events=this._events.filter(function(o){var r=!0;return t&&t!==o.target&&(r=!1),n&&n!==o.type&&(r=!1),i&&i!==o.handler&&(r=!1),r&&e._doRemove(o.target,o.type,o.handler),!r})}},{key:"destroy",value:function(){this._events.forEach(function(t){return e._doRemove(t.target,t.type,t.handler)}),this._events=[]}}],[{key:"_doRemove",value:function(e,t,n){e.removeEventListener(t,n,!1)}}]),e}(),h="keydown",p="mousedown",f="focusin",g=function(){function e(t){a(this,e),this.settings={popup:"right",layout:"default",alpha:!0,editor:!0,editorFormat:"hex",cancelButton:!1,defaultColor:"#0cf"},this._events=new u,this.onChange=null,this.onDone=null,this.onOpen=null,this.onClose=null,this.setOptions(t)}return s(e,[{key:"setOptions",value:function(e){var t=this;if(e){var n=this.settings;if(e instanceof HTMLElement)n.parent=e;else{n.parent&&e.parent&&n.parent!==e.parent&&(this._events.remove(n.parent),this._popupInited=!1),function(e,t,n){for(var i in e)n&&n.indexOf(i)>=0||(t[i]=e[i])}(e,n),e.onChange&&(this.onChange=e.onChange),e.onDone&&(this.onDone=e.onDone),e.onOpen&&(this.onOpen=e.onOpen),e.onClose&&(this.onClose=e.onClose);var i=e.color||e.colour;i&&this._setColor(i)}var o=n.parent;if(o&&n.popup&&!this._popupInited){var a=function(e){return t.openHandler(e)};this._events.add(o,"click",a),r(this._events,o,[" ","Spacebar","Enter"],a),this._popupInited=!0}else e.parent&&!n.popup&&this.show()}}},{key:"openHandler",value:function(e){if(this.show()){e&&e.preventDefault(),this.settings.parent.style.pointerEvents="none";var t=e&&e.type===h?this._domEdit:this.domElement;setTimeout(function(){return t.focus()},100),this.onOpen&&this.onOpen(this.colour)}}},{key:"closeHandler",value:function(e){var t=e&&e.type,n=!1;if(e)if(t===p||t===f){var i=(this.__containedEvent||0)+100;e.timeStamp>i&&(n=!0)}else o(e),n=!0;else n=!0;n&&this.hide()&&(this.settings.parent.style.pointerEvents="",t!==p&&this.settings.parent.focus(),this.onClose&&this.onClose(this.colour))}},{key:"movePopup",value:function(e,t){this.closeHandler(),this.setOptions(e),t&&this.openHandler()}},{key:"setColor",value:function(e,t){this._setColor(e,{silent:t})}},{key:"_setColor",value:function(e,t){if("string"==typeof e&&(e=e.trim()),e){t=t||{};var n=void 0;try{n=new d(e)}catch(e){if(t.failSilently)return;throw e}if(!this.settings.alpha){var i=n.hsla;i[3]=1,n.hsla=i}this.colour=this.color=n,this._setHSLA(null,null,null,null,t)}}},{key:"setColour",value:function(e,t){this.setColor(e,t)}},{key:"show",value:function(){if(!this.settings.parent)return!1;if(this.domElement){var e=this._toggleDOM(!0);return this._setPosition(),e}var n=this.settings.template||'
    ',o=t(n);return this.domElement=o,this._domH=i(".picker_hue",o),this._domSL=i(".picker_sl",o),this._domA=i(".picker_alpha",o),this._domEdit=i(".picker_editor input",o),this._domSample=i(".picker_sample",o),this._domOkay=i(".picker_done button",o),this._domCancel=i(".picker_cancel button",o),o.classList.add("layout_"+this.settings.layout),this.settings.alpha||o.classList.add("no_alpha"),this.settings.editor||o.classList.add("no_editor"),this.settings.cancelButton||o.classList.add("no_cancel"),this._ifPopup(function(){return o.classList.add("popup")}),this._setPosition(),this.colour?this._updateUI():this._setColor(this.settings.defaultColor),this._bindEvents(),!0}},{key:"hide",value:function(){return this._toggleDOM(!1)}},{key:"destroy",value:function(){this._events.destroy(),this.domElement&&this.settings.parent.removeChild(this.domElement)}},{key:"_bindEvents",value:function(){function e(e,t,n){a.add(e,t,n)}var t=this,i=this,o=this.domElement,a=this._events;e(o,"click",function(e){return e.preventDefault()}),n(a,this._domH,function(e,t){return i._setHSLA(e)}),n(a,this._domSL,function(e,t){return i._setHSLA(null,e,1-t)}),this.settings.alpha&&n(a,this._domA,function(e,t){return i._setHSLA(null,null,null,1-t)});var s=this._domEdit;e(s,"input",function(e){i._setColor(this.value,{fromEditor:!0,failSilently:!0})}),e(s,"focus",function(e){var t=this;t.selectionStart===t.selectionEnd&&t.select()}),this._ifPopup(function(){var n=function(e){return t.closeHandler(e)};e(window,p,n),e(window,f,n),r(a,o,["Esc","Escape"],n);var i=function(e){t.__containedEvent=e.timeStamp};e(o,p,i),e(o,f,i),e(t._domCancel,"click",n)});var l=function(e){t._ifPopup(function(){return t.closeHandler(e)}),t.onDone&&t.onDone(t.colour)};e(this._domOkay,"click",l),r(a,o,["Enter"],l)}},{key:"_setPosition",value:function(){var e=this.settings.parent,t=this.domElement;e!==t.parentNode&&e.appendChild(t),this._ifPopup(function(n){"static"===getComputedStyle(e).position&&(e.style.position="relative");var i=!0===n?"popup_right":"popup_"+n;["popup_top","popup_bottom","popup_left","popup_right"].forEach(function(e){e===i?t.classList.add(e):t.classList.remove(e)}),t.classList.add(i)})}},{key:"_setHSLA",value:function(e,t,n,i,o){o=o||{};var r=this.colour,a=r.hsla;[e,t,n,i].forEach(function(e,t){(e||0===e)&&(a[t]=e)}),r.hsla=a,this._updateUI(o),this.onChange&&!o.silent&&this.onChange(r)}},{key:"_updateUI",value:function(e){function t(e,t,n){t.style.left=100*n+"%"}function n(e,t,n){t.style.top=100*n+"%"}if(this.domElement){e=e||{};var o=this.colour,r=o.hsla,a="hsl("+360*r[0]+", 100%, 50%)",s=o.hslString,l=o.hslaString,c=this._domH,d=this._domSL,u=this._domA,h=i(".picker_selector",c),p=i(".picker_selector",d),f=i(".picker_selector",u);t(c,h,r[0]),this._domSL.style.backgroundColor=this._domH.style.color=a,t(d,p,r[1]),n(d,p,1-r[2]),d.style.color=s,n(u,f,1-r[3]);var g=s,m=g.replace("hsl","hsla").replace(")",", 0)"),y="linear-gradient("+[g,m]+")";if(this._domA.style.background=y+", linear-gradient(45deg, lightgrey 25%, transparent 25%, transparent 75%, lightgrey 75%) 0 0 / 2em 2em,\n linear-gradient(45deg, lightgrey 25%, white 25%, white 75%, lightgrey 75%) 1em 1em / 2em 2em",!e.fromEditor){var v=this.settings.editorFormat,b=this.settings.alpha,C=void 0;switch(v){case"rgb":C=o.printRGB(b);break;case"hsl":C=o.printHSL(b);break;default:C=o.printHex(b)}this._domEdit.value=C}this._domSample.style.color=l}}},{key:"_ifPopup",value:function(e,t){this.settings.parent&&this.settings.popup?e&&e(this.settings.popup):t&&t()}},{key:"_toggleDOM",value:function(e){var t=this.domElement;if(!t)return!1;var n=e?"":"none",i=t.style.display!==n;return i&&(t.style.display=n),i}}]),e}(),m=document.createElement("style");return m.textContent='.picker_wrapper.no_alpha .picker_alpha{display:none}.picker_wrapper.no_editor .picker_editor{position:absolute;z-index:-1;opacity:0}.picker_wrapper.no_cancel .picker_cancel{display:none}.layout_default.picker_wrapper{display:flex;flex-flow:row wrap;justify-content:space-between;align-items:stretch;font-size:10px;width:25em;padding:.5em}.layout_default.picker_wrapper input,.layout_default.picker_wrapper button{font-size:1rem}.layout_default.picker_wrapper>*{margin:.5em}.layout_default.picker_wrapper::before{content:"";display:block;width:100%;height:0;order:1}.layout_default .picker_slider,.layout_default .picker_selector{padding:1em}.layout_default .picker_hue{width:100%}.layout_default .picker_sl{flex:1 1 auto}.layout_default .picker_sl::before{content:"";display:block;padding-bottom:100%}.layout_default .picker_editor{order:1;width:6.5rem}.layout_default .picker_editor input{width:100%;height:100%}.layout_default .picker_sample{order:1;flex:1 1 auto}.layout_default .picker_done,.layout_default .picker_cancel{order:1}.picker_wrapper{box-sizing:border-box;background:#f2f2f2;box-shadow:0 0 0 1px silver;cursor:default;font-family:sans-serif;color:#444;pointer-events:auto}.picker_wrapper:focus{outline:none}.picker_wrapper button,.picker_wrapper input{box-sizing:border-box;border:none;box-shadow:0 0 0 1px silver;outline:none}.picker_wrapper button:focus,.picker_wrapper button:active,.picker_wrapper input:focus,.picker_wrapper input:active{box-shadow:0 0 2px 1px #1e90ff}.picker_wrapper button{padding:.4em .6em;cursor:pointer;background-color:#f5f5f5;background-image:linear-gradient(0deg, gainsboro, transparent)}.picker_wrapper button:active{background-image:linear-gradient(0deg, transparent, gainsboro)}.picker_wrapper button:hover{background-color:#fff}.picker_selector{position:absolute;z-index:1;display:block;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);border:2px solid #fff;border-radius:100%;box-shadow:0 0 3px 1px #67b9ff;background:currentColor;cursor:pointer}.picker_slider .picker_selector{border-radius:2px}.picker_hue{position:relative;background-image:linear-gradient(90deg, red, yellow, lime, cyan, blue, magenta, red);box-shadow:0 0 0 1px silver}.picker_sl{position:relative;box-shadow:0 0 0 1px silver;background-image:linear-gradient(180deg, white, rgba(255, 255, 255, 0) 50%),linear-gradient(0deg, black, rgba(0, 0, 0, 0) 50%),linear-gradient(90deg, #808080, rgba(128, 128, 128, 0))}.picker_alpha,.picker_sample{position:relative;background:linear-gradient(45deg, lightgrey 25%, transparent 25%, transparent 75%, lightgrey 75%) 0 0/2em 2em,linear-gradient(45deg, lightgrey 25%, white 25%, white 75%, lightgrey 75%) 1em 1em/2em 2em;box-shadow:0 0 0 1px silver}.picker_alpha .picker_selector,.picker_sample .picker_selector{background:none}.picker_editor input{font-family:monospace;padding:.2em .4em}.picker_sample::before{content:"";position:absolute;display:block;width:100%;height:100%;background:currentColor}.picker_arrow{position:absolute;z-index:-1}.picker_wrapper.popup{position:absolute;z-index:2;margin:1.5em}.picker_wrapper.popup,.picker_wrapper.popup .picker_arrow::before,.picker_wrapper.popup .picker_arrow::after{background:#f2f2f2;box-shadow:0 0 10px 1px rgba(0,0,0,.4)}.picker_wrapper.popup .picker_arrow{width:3em;height:3em;margin:0}.picker_wrapper.popup .picker_arrow::before,.picker_wrapper.popup .picker_arrow::after{content:"";display:block;position:absolute;top:0;left:0;z-index:-99}.picker_wrapper.popup .picker_arrow::before{width:100%;height:100%;-webkit-transform:skew(45deg);transform:skew(45deg);-webkit-transform-origin:0 100%;transform-origin:0 100%}.picker_wrapper.popup .picker_arrow::after{width:150%;height:150%;box-shadow:none}.popup.popup_top{bottom:100%;left:0}.popup.popup_top .picker_arrow{bottom:0;left:0;-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.popup.popup_bottom{top:100%;left:0}.popup.popup_bottom .picker_arrow{top:0;left:0;-webkit-transform:rotate(90deg) scale(1, -1);transform:rotate(90deg) scale(1, -1)}.popup.popup_left{top:0;right:100%}.popup.popup_left .picker_arrow{top:0;right:0;-webkit-transform:scale(-1, 1);transform:scale(-1, 1)}.popup.popup_right{top:0;left:100%}.popup.popup_right .picker_arrow{top:0;left:0}',document.documentElement.firstElementChild.appendChild(m),g.StyleElement=m,g}()},9857:function(e,t,n){"use strict";function i(e){return e>=N&&e<=M}function o(e){return re.test(e)}function r(e){return ae.test(e)||e&&s(e.charCodeAt(0))}function a(e){return e===k||e===_||e===S||e===T}function s(e){return l(e)||d(e)}function l(e){return e===$||e===Q||e===ee}function c(e){return e===$}function d(e){return e===O||e===te||e===ne||e===ie||e===oe}function u(e){return e===O}function h(e,t,n){return n=2=e.length;(o||(r(e[ae])||a?ce=p(ce,":"):re()),t())||(o||a?ce+="null":re())}return e.charCodeAt(ae)===C?(ce+="}",ae++):ce=p(ce,"}"),!0}()||function(){if(e.charCodeAt(ae)!==x)return!1;ce+="[",ae++,n();for(var i=!0;aet&&(t=e.slice(t,ae),n=/^0\d/.test(t),ce+=n?'"'.concat(t,'"'):t,!0)}()||te("true","true")||te("false","false")||te("null","null")||te("True","true")||te("False","false")||te("None","null")||ne();return n(),o}function n(){for(f();function(){if(e.charCodeAt(ae)===y&&e.charCodeAt(ae+1)===v){for(;ae=X&&n<=Y||n===U||n===J||n===q;)i+=t?e[ae]:" ",ae++;return 0=e.length)return h?(C=p(C,'"'),ce+=C,!0):(ae=v,ce=ce.substring(0,b),Q(!0));if(y(e.charCodeAt(ae))){var x=ae,I=C.length;if(C+='"',ae++,ce+=C,n(),h||ae>=e.length||o(e.charAt(ae))||s(e.charCodeAt(ae)))return ee(),!0;if(o(e.charAt(function(t){for(var n=t;0=N&&r<=M||r>=H&&r<=Z||r>=W&&r<=z);)w++;if(6===w)C+=e.slice(ae,ae+6),ae+=6;else{if(!(ae+w>=e.length))throw I=void 0,I=e.slice(ae,ae+6),new g('Invalid unicode character "'.concat(I,'"'),ae);ae=e.length}}else C+=x,ae+=2;else{var A=e.charAt(ae),k=e.charCodeAt(ae);if(k===$&&e.charCodeAt(ae-1)!==m)C+="\\"+A;else if((i=k)===_||i===T||i===S||i===E||i===R)C+=se[A];else{if(!(32<=(i=k)&&i<=1114111))throw k=void 0,k=A,new g("Invalid character "+JSON.stringify(k),ae);C+=A}ae++}}f&&O()}}return!1}function ee(){for(n();e.charCodeAt(ae)===L;){!0,ae++,n();var t=(ce=h(ce,'"',!0)).length,i=Q();ce=i?(i=ce,t=t,o=1,i.substring(0,t)+i.substring(t+o)):p(ce,'"')}var o}function te(t,n){return e.slice(ae,ae+t.length)===t&&(ce+=n,ae+=t.length,!0)}function ne(){for(var n=ae;aen){if(e.charCodeAt(ae)===w)ae++,t(),e.charCodeAt(ae)===A&&(ae++,e.charCodeAt(ae)===P)&&ae++;else{for(;a(e.charCodeAt(ae-1))&&0=e.length)return ce+=e.slice(t,ae)+"0",1;ie(t)}function re(){throw new g("Colon expected",ae)}var ae=0,ce="";if(!t())throw new g("Unexpected end of json string",e.length);var de=k(D);if(de&&n(),r(e[ae])&&/[,\n][ \t\r]*$/.test(ce)){de||(ce=p(ce,","));for(var ue=!0,he=!0;he;)ue?ue=!1:k(D)||(ce=p(ce,",")),he=t();he||(ce=h(ce,",")),ce="[\n".concat(ce,"\n]")}else de&&(ce=h(ce,","));for(;e.charCodeAt(ae)===C||e.charCodeAt(ae)===I;)ae++,n();if(ae>=e.length)return ce;throw new g("Unexpected character "+JSON.stringify(e[ae]),ae)}n.d(t,{m:function(){return f}});var g=function(e){function t(e,n){var i;return _classCallCheck(this,t),i=_possibleConstructorReturn(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e+" at position "+n)),i.position=n,i}return _inherits(t,e),t}(Error),m=92,y=47,v=42,b=123,C=125,x=91,I=93,w=40,A=41,k=32,_=10,S=9,T=13,E=8,R=12,$=34,L=43,j=45,O=39,N=48,M=57,D=44,B=46,F=58,P=59,H=65,W=97,V=69,G=101,Z=70,z=102,K=160,X=8192,Y=8202,U=8239,J=8287,q=12288,Q=8220,ee=8221,te=8216,ne=8217,ie=96,oe=180,re=/^[,:[\]/{}()\n+]$/,ae=/^[[{\w-]$/,se={"\b":"\\b","\f":"\\f","\n":"\\n","\r":"\\r","\t":"\\t"},le={'"':'"',"\\":"\\","/":"/",b:"\b",f:"\f",n:"\n",r:"\r",t:"\t"}},3420:function(e){"use strict";e.exports=JSON.parse('{"$schema":"http://json-schema.org/draft-07/schema#","$id":"https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#","description":"Meta-schema for $data reference (JSON Schema extension proposal)","type":"object","required":["$data"],"properties":{"$data":{"type":"string","anyOf":[{"format":"relative-json-pointer"},{"format":"json-pointer"}]}},"additionalProperties":false}')},5207:function(e){"use strict";e.exports=JSON.parse('{"id":"http://json-schema.org/draft-04/schema#","$schema":"http://json-schema.org/draft-04/schema#","description":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"positiveInteger":{"type":"integer","minimum":0},"positiveIntegerDefault0":{"allOf":[{"$ref":"#/definitions/positiveInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"minItems":1,"uniqueItems":true}},"type":"object","properties":{"id":{"type":"string"},"$schema":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"default":{},"multipleOf":{"type":"number","minimum":0,"exclusiveMinimum":true},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"boolean","default":false},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"boolean","default":false},"maxLength":{"$ref":"#/definitions/positiveInteger"},"minLength":{"$ref":"#/definitions/positiveIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"anyOf":[{"type":"boolean"},{"$ref":"#"}],"default":{}},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":{}},"maxItems":{"$ref":"#/definitions/positiveInteger"},"minItems":{"$ref":"#/definitions/positiveIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"maxProperties":{"$ref":"#/definitions/positiveInteger"},"minProperties":{"$ref":"#/definitions/positiveIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"anyOf":[{"type":"boolean"},{"$ref":"#"}],"default":{}},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"enum":{"type":"array","minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"dependencies":{"exclusiveMaximum":["maximum"],"exclusiveMinimum":["minimum"]},"default":{}}')},6801:function(e){"use strict";e.exports=JSON.parse('{"$schema":"http://json-schema.org/draft-06/schema#","$id":"http://json-schema.org/draft-06/schema#","title":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"nonNegativeInteger":{"type":"integer","minimum":0},"nonNegativeIntegerDefault0":{"allOf":[{"$ref":"#/definitions/nonNegativeInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"uniqueItems":true,"default":[]}},"type":["object","boolean"],"properties":{"$id":{"type":"string","format":"uri-reference"},"$schema":{"type":"string","format":"uri"},"$ref":{"type":"string","format":"uri-reference"},"title":{"type":"string"},"description":{"type":"string"},"default":{},"examples":{"type":"array","items":{}},"multipleOf":{"type":"number","exclusiveMinimum":0},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"number"},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"number"},"maxLength":{"$ref":"#/definitions/nonNegativeInteger"},"minLength":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"$ref":"#"},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":{}},"maxItems":{"$ref":"#/definitions/nonNegativeInteger"},"minItems":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"contains":{"$ref":"#"},"maxProperties":{"$ref":"#/definitions/nonNegativeInteger"},"minProperties":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"$ref":"#"},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"propertyNames":{"$ref":"#"},"const":{},"enum":{"type":"array","minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"default":{}}')},8198:function(e){"use strict";e.exports=JSON.parse('{"$schema":"http://json-schema.org/draft-07/schema#","$id":"http://json-schema.org/draft-07/schema#","title":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"nonNegativeInteger":{"type":"integer","minimum":0},"nonNegativeIntegerDefault0":{"allOf":[{"$ref":"#/definitions/nonNegativeInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"uniqueItems":true,"default":[]}},"type":["object","boolean"],"properties":{"$id":{"type":"string","format":"uri-reference"},"$schema":{"type":"string","format":"uri"},"$ref":{"type":"string","format":"uri-reference"},"$comment":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"default":true,"readOnly":{"type":"boolean","default":false},"examples":{"type":"array","items":true},"multipleOf":{"type":"number","exclusiveMinimum":0},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"number"},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"number"},"maxLength":{"$ref":"#/definitions/nonNegativeInteger"},"minLength":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"$ref":"#"},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":true},"maxItems":{"$ref":"#/definitions/nonNegativeInteger"},"minItems":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"contains":{"$ref":"#"},"maxProperties":{"$ref":"#/definitions/nonNegativeInteger"},"minProperties":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"$ref":"#"},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"propertyNames":{"format":"regex"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"propertyNames":{"$ref":"#"},"const":true,"enum":{"type":"array","items":true,"minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"contentMediaType":{"type":"string"},"contentEncoding":{"type":"string"},"if":{"$ref":"#"},"then":{"$ref":"#"},"else":{"$ref":"#"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"default":true}')}},n={},e.amdD=function(){throw new Error("define cannot be used indirect")},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,{a:n}),n},e.d=function(t,n){for(var i in n)e.o(n,i)&&!e.o(t,i)&&Object.defineProperty(t,i,{enumerable:!0,get:n[i]})},e.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},e.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},e.nmd=function(e){return e.paths=[],e.children||(e.children=[]),e},e(1346);var t,n}),layui.define(["table","jquery","form"],function(e){"use strict";var t=layui.jquery,n=layui.table,i=layui.form,o=function(){this.v="1.1.0"};o.prototype.render=function(e){var o=t(e.elem),r=e.table.done||function(){};e.searchKey=e.searchKey||"keyword",e.searchPlaceholder=e.searchPlaceholder||"关键词搜索",e.checkedKey=e.checkedKey||"",e.table.page=e.table.page||!0,e.table.height=e.height||315,e.width=e.width||"530",e.searchType=e.searchType||"1",e.searchList=e.searchList||[{key:e.searchKey,placeholder:e.searchPlaceholder}],o.off("click").on("click",function(a){function s(i,o,r){for(var a=0;a=1)return!1;var h=o.offset().top+o.outerHeight()+"px",p=o.offset().left+"px",f="tableSelect_table_"+(new Date).getTime(),g='
    ';g+='
    ',g+='
    ',2==e.searchType?t.each(e.searchList,function(e,t){g+=''}):g+='',g+='',g+="
    ",g+='',g+="
    ",g+='
    ',g+="
    ",g=t(g),t("body").append(g);var m=[];e.table.elem="#"+f,e.table.id=f, -e.table.done=function(e,t,n){l(e,t,n),s(e,t,n),r(e,t,n)};var y=n.render(e.table);n.on("radio("+f+")",function(t){e.checkedKey&&(m=n.checkStatus(f).data),c(n.checkStatus(f).data.length)}),n.on("checkbox("+f+")",function(t){if(e.checkedKey){if(t.checked)for(var i=0;it(window).height(),b=o.offset().left+g.outerWidth()>t(window).width();v&&g.css({top:"auto",bottom:"0px"}),b&&g.css({left:"auto",right:"5px"}),i.on("submit(tableSelect_btn_search)",function(e){return y.reload({where:e.field,page:{curr:1}}),!1}),n.on("rowDouble("+f+")",function(e){u({data:[e.data]})}),g.find(".tableSelect_btn_select").on("click",function(){var e=n.checkStatus(f);m.length>1&&(e.data=m),u(e)}),t(document).mouseup(function(i){id=t(e.elem).attr("id");var o=t(id+",.tableSelect");o.is(i.target)||0!==o.has(i.target).length||(g.remove(),delete n.cache[f],m=[])})})},o.prototype.hide=function(e){t(".tableSelect").remove()};var o=new o;window.top==window.self&&t(window).scroll(function(){o.hide()}),e("tableSelect",o)}),define("tableSelect",function(){}),define("plugins/require-css/css.min!plugins/lay-module/tableFilter/tableFilter",[],function(){}),layui.define(["table","jquery","form","laydate"],function(e){var t=layui.jquery,n=layui.table,i=layui.form,o=layui.laydate,r={v:"1.1.0"};r.cache={},r.render=function(e){var a=t(e.elem||"#table"),s=a.attr("id")||"table_"+(new Date).getTime(),l=e.filters||[],c=e.parent||"body",d=e.mode||"local";e.done=e.done||function(){},r.cache[s]={};var u=function(){if("local"==d){var u=r.getShowTrIndex(a,s,l);if(u.length>0){a.next().find(".layui-table-body tr").each(function(e,n){-1!=t.inArray(t(n).data("index"),u)?t(n).removeClass("layui-hide"):t(n).addClass("layui-hide")})}else a.next().find(".layui-table-body tr").removeClass("layui-hide");r.fixAll(a),n.resize(s)}layui.each(l,function(u,h){var p=h.field,f=h.name||h.field,g=h.type||"input",m=h.data||[],y=h.url||"",v=a.next().find('.layui-table-header th[data-field="'+p+'"]'),b="input"==g?"layui-icon-search":"layui-icon-down",C=t('');v.find(".layui-table-cell").append(C),r.cache[s][f]?C.addClass("tableFilter-has"):C.removeClass("tableFilter-has"),C.on("click",function(u){u.stopPropagation(),h.data||y||"input"==g||(m=r.eachTds(a,p));var v=t(this).offset().top+t(c).scrollTop()+t(this).outerHeight()+5+"px",b="date"==g?530:164,x=t(this).offset().left-(t("body").outerWidth(!0)-t(c).outerWidth(!0))-b+"px",I=t('
    ');if("input"==g&&I.find("form").append(''),"checkbox"==g&&(I.find("form").append("
      "),y||layui.each(m,function(e,t){I.find("ul").append('
    • ')})),"radio"==g&&(I.find("form").append('
        '),y||(I.find("ul").append('
      • '),layui.each(m,function(e,t){I.find("ul").append('
      • ')}))),"date"==g&&(I.find("form").append('
        '),I.find("form").append('')),I.find("form").append(''),I.find("form").append(''),t(this).hasClass("tableFilter-has")&&I.find(".filter-del").removeClass("layui-btn-disabled").removeAttr("disabled","disabled"),t(c).append(I),i.val("table-filter-form",r.toLayuiFrom(s,f,g)),i.render(null,"table-filter-form"),"date"==g&&o.render({elem:".layui-table-filter-date",range:!0,type:"date",value:t(".layui-table-filter-date").next().val(),position:"static",showBottom:!1,change:function(e,n,i){t(".layui-table-filter-date").next().val(e)}}),I.find('form input[type="search"]').focus().select(),("checkbox"==g||"radio"==g)&&y){var w=I.find(".layui-table-filter-box ul");w.append('
        '),t.getJSON(y+"?_t="+(new Date).getTime(),function(e,t,n){w.empty(),"radio"==g&&w.append('
      • '),layui.each(e.data,function(e,t){"checkbox"==g&&w.append('
      • '),"radio"==g&&w.append('
      • ')}),i.render(null,"table-filter-form"),i.val("table-filter-form",r.toLayuiFrom(s,f,g))})}i.on("submit(tableFilter)",function(i){if("checkbox"==g){var o=[];for(var c in i.field)o.push(i.field[c]);i.field[f]=o}if(r.cache[s][f]=i.field[f],r.cache[s][f].length>0?C.addClass("tableFilter-has"):C.removeClass("tableFilter-has"),"local"==d){var u=r.getShowTrIndex(a,s,l);if(u.length>0||i.field[f].length>0){a.next().find(".layui-table-body tr").each(function(e,n){-1!=t.inArray(t(n).data("index"),u)?t(n).removeClass("layui-hide"):t(n).addClass("layui-hide")})}else a.next().find(".layui-table-body tr").removeClass("layui-hide");r.updataTotal(a),r.upNumbers(a),r.uncheck(a),n.resize(s)}else if("api"==d){var h={},p={};for(var c in r.cache[s]){var m=c,y=r.cache[s][c];h[m]=y,p[m]="LIKE",t.isArray(y)?(h[m]=y,p[m]="IN"):h[m]=y}$filter={filter:JSON.stringify(h),op:JSON.stringify(p)},n.reload(s,{where:$filter})}return e.done(r.cache[s]),I.remove(),!1}),I.find(".layui-table-filter-box .filter-del").on("click",function(i){if(delete r.cache[s][f],C.removeClass("tableFilter-has"),"local"==d){var o=r.getShowTrIndex(a,s,l);if(o.length>0){a.next().find(".layui-table-body tr").each(function(e,n){-1!=t.inArray(t(n).data("index"),o)?t(n).removeClass("layui-hide"):t(n).addClass("layui-hide")})}else a.next().find(".layui-table-body tr").removeClass("layui-hide");r.updataTotal(a),r.upNumbers(a),r.uncheck(a),n.resize(s)}else if("api"==d){var c={};c[f]="",n.reload(s,{where:c})}e.done(r.cache[s]),I.remove()}),t(document).mouseup(function(e){var n=t(".layui-table-filter-view");n.is(e.target)||0!==n.has(e.target).length||I.remove()})})})};return u(),{config:e,reload:function(e){u(),r.upNumbers(a)}}},r.eachTds=function(e,n){var i=[],o=[];return e.next().find('.layui-table-body td[data-field="'+n+'"]').each(function(e,n){i.push(t.trim(n.innerText))}),i=r.tool.uniqueObjArray(i),layui.each(i,function(e,t){o.push({key:t,value:t})}),o},r.getShowTrIndex=function(e,n,i){var o=[],a=r.cache[n];for(var s in a){var l=s,c=a[s];layui.each(i,function(e,t){l==t.name&&(l=t.field)});var d=e.next().find('.layui-table-body td[data-field="'+l+'"]'),u=[];d.each(function(e,n){if(t.isArray(c))t.inArray(t.trim(n.innerText),c)>=0&&c&&c.length>0&&u.push(t(n).parent().data("index"));else if(c.indexOf(" - ")>=0){var i=t.trim(n.innerText),o=c.split(" - ")[0],a=c.split(" - ")[1];r.tool.isDuringDate(i,o,a)&&u.push(t(n).parent().data("index"))}else t.trim(n.innerText).indexOf(c)>=0&&u.push(t(n).parent().data("index"))}),o=o.length<=0?u:u.length>0?r.tool.getSameArray(o,u):t.isArray(c)?o:[]}return r.tool.uniqueObjArray(o)},r.toLayuiFrom=function(e,t,n){var i=JSON.stringify(r.cache[e]);return i=JSON.parse(i),"checkbox"==n&&(layui.each(i[t],function(e,n){i[t+"["+n+"]"]=!0}),delete i[t]),i},r.updataTotal=function(e){var i=e.attr("id");n.eachCols(i,function(n,i){if(i.totalRow){var o=0;e.next().find('.layui-table-body td[data-field="'+i.field+'"]').each(function(e,n){t(n).parent().hasClass("layui-hide")||(o=(10*o+10*Number(t.trim(n.innerText)))/10)});e.next().find('.layui-table-total td[data-field="'+i.field+'"]').each(function(e,n){t(n).find(".layui-table-cell").html(o||"0")})}})},r.upNumbers=function(e){var n=e.next().find(".layui-laypage-curr").text();n=Number(n||"1");var i=e.next().find(".layui-laypage-limits select").val();i=Number(i);var o=e.next().find(".layui-table-main tr"),r=1==n?0:i*(n-1);if(o.each(function(e,n){t(n).hasClass("layui-hide")||(r+=1,t(n).find(".laytable-cell-numbers").html(r))}),e.next().find(".layui-table-fixed").length>=1){var a=e.next().find(".layui-table-fixed .layui-table-body tr"),s=1==n?0:i*(n-1);a.each(function(e,n){t(n).hasClass("layui-hide")||(s+=1,t(n).find(".laytable-cell-numbers").html(s))})}},r.uncheck=function(e){e.attr("id"),e.attr("lay-filter");e.next().find(".layui-table-fixed-l tr").each(function(e,n){t(n).find("[name='layTableCheckbox']").prop("checked")&&t(n).find(".layui-form-checked i").click()})},r.fixAll=function(e){var i=e.attr("id"),o=e.attr("lay-filter"),r=e.next().find(".layui-table-main tr");n.on("checkbox("+o+")",function(e){if("all"==e.type){var o=n.cache[i];r.each(function(e,n){t(n).hasClass("layui-hide")&&(o[e].LAY_CHECKED=!1)})}})},r.hide=function(){t(".layui-table-filter-view").remove()},r.tool={uniqueObjArray:function(e,t){var n=[],i=[];if(0==e.length)return e;if(t){for(var o=0;o=o&&i<=r}},e("tableFilter",r)}),define("tableFilter",["css!plugins/lay-module/tableFilter/tableFilter.css"],function(){}),layui.define(["laypage","form"],function(e){"use strict";var t=function(){this.v="0.1.beta"},n=layui.jquery;layui.laypage,layui.form;t.prototype.render=function(e){var i=e,o=i.elem,r=null==i.type?"fontClass":i.type,a=i.page,s=null==s?12:i.limit,l=null==i.search||i.search,c=i.click,d={},u=(new Date).getTime(),h="layui-select-title-"+u,p="layui-iconpicker-"+u,f="layui-iconpicker-body-"+u,g="layui-iconpicker-page-"+u,m="layui-iconpicker-list-box",y="layui-form-selected",v=e.value||n(o).val()||"layui-icon layui-icon-circle-dot",b=' '+v+"";console.log(b);var C={init:function(){return d=x.getData[r](),C.hideElem().createSelect().createBody().toggleSelect(),x.loadCss(),C},hideElem:function(){return n(o).hide(),C},createSelect:function(){var e='
        '+b+'
        123
        ';return n(o).after(e),C},toggleSelect:function(){var e="#"+h+" .layui-iconpicker-item,#"+h+" .layui-iconpicker-item .layui-edge";return C.event("click",e,function(e){var t=n("#"+p);t.hasClass(y)?t.removeClass(y).addClass("layui-unselect"):t.addClass(y).removeClass("layui-unselect"),e.stopPropagation()}),C},createBody:function(){var e="";l&&(e='');var t='
        '+e+'
        ';return n("#"+p).find(".layui-anim").eq(0).html(t),C.search().createList().check().page(),C},createList:function(e){for(var t=d,i=t.length,o="",r=n('
        '),l=s,c=i%l==0?i/l:parseInt(i/l+1),u=[],h=0;h';v+='',v+="
        ",u.push(v)}}i=u.length,c=i%l==0?i/l:parseInt(i/l+1);for(var h=0;h'),x=h*l;x<(h+1)*l&&x无数据

        '),a&&(n("#"+f).addClass("layui-iconpicker-body-page"),o='
        1/'+c+' ('+i+')
        '),n("#"+p).find(".layui-anim").find("."+m).html("").append(r).append(o),C},page:function(){var e="#"+g+" .layui-iconpicker-page-operate .layui-icon";return n(e).unbind("click"),C.event("click",e,function(t){var i=t.currentTarget,o=parseInt(n("#"+g+"-pages").html()),r=void 0!==n(i).attr("prev"),a=(parseInt(n(i).attr("data-index")),n("#"+g+"-current")),s=parseInt(a.html());r&&s>1?(s-=1,n(e+"[prev]").attr("data-index",s)):!r&&s.layui-iconpicker {max-width: 280px;}.layui-iconpicker .layui-anim{display:none;position:absolute;left:0;top:42px;padding:5px 0;z-index:899;min-width:100%;border:1px solid #d2d2d2;max-height:300px;overflow-y:auto;background-color:#fff;border-radius:2px;box-shadow:0 2px 4px rgba(0,0,0,.12);box-sizing:border-box;}.layui-iconpicker-item{border:1px solid #e6e6e6;height:38px;border-radius:4px;cursor:pointer;position:relative;}.layui-iconpicker-icon{border-right:1px solid #e6e6e6;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;width:85%;height:100%;float:left;text-align:center;background:#fff;transition:all .3s;}.layui-iconpicker-icon i{line-height:38px;font-size:18px;}.layui-iconpicker-item:hover{border-color:#D2D2D2!important;}.layui-iconpicker-item:hover .layui-iconpicker-icon{border-color:#D2D2D2!important;}.layui-iconpicker.layui-form-selected .layui-anim{display:block;}.layui-iconpicker-body{padding:6px;}.layui-iconpicker .layui-iconpicker-list{background-color:#fff;border:1px solid #ccc;border-radius:4px;}.layui-iconpicker .layui-iconpicker-icon-item{display:inline-block;width:21.1%;line-height:36px;text-align:center;cursor:pointer;vertical-align:top;height:36px;margin:4px;border:1px solid #ddd;border-radius:2px;transition:300ms;}.layui-iconpicker .layui-iconpicker-icon-item i.layui-icon{font-size:17px;}.layui-iconpicker .layui-iconpicker-icon-item:hover{background-color:#eee;border-color:#ccc;-webkit-box-shadow:0 0 2px #aaa,0 0 2px #fff inset;-moz-box-shadow:0 0 2px #aaa,0 0 2px #fff inset;box-shadow:0 0 2px #aaa,0 0 2px #fff inset;text-shadow:0 0 1px #fff;}.layui-iconpicker-search{position:relative;margin:0 0 6px 0;border:1px solid #e6e6e6;border-radius:2px;transition:300ms;}.layui-iconpicker-search:hover{border-color:#D2D2D2!important;}.layui-iconpicker-search .layui-input{cursor:text;display:inline-block;width:86%;border:none;padding-right:0;margin-top:1px;}.layui-iconpicker-search .layui-icon{position:absolute;top:11px;right:4%;}.layui-iconpicker-tips{text-align:center;padding:8px 0;cursor:not-allowed;}.layui-iconpicker-page{margin-top:6px;margin-bottom:-6px;font-size:12px;padding:0 2px;}.layui-iconpicker-page-count{display:inline-block;}.layui-iconpicker-page-operate{display:inline-block;float:right;cursor:default;}.layui-iconpicker-page-operate .layui-icon{font-size:12px;cursor:pointer;}.layui-iconpicker-body-page .layui-iconpicker-icon-limit{display:none;}.layui-iconpicker-body-page .layui-iconpicker-icon-limit:first-child{display:block;}')},getData:{fontClass:function(){return["layui-icon-github","layui-icon-moon","layui-icon-error","layui-icon-success","layui-icon-question","layui-icon-lock","layui-icon-eye","layui-icon-eye-invisible","layui-icon-clear","layui-icon-backspace","layui-icon-disabled","layui-icon-tips-fill","layui-icon-test","layui-icon-music","layui-icon-chrome","layui-icon-firefox","layui-icon-edge","layui-icon-ie","layui-icon-heart-fill","layui-icon-heart","layui-icon-light","layui-icon-time","layui-icon-bluetooth","layui-icon-at","layui-icon-mute","layui-icon-mike","layui-icon-key","layui-icon-gift","layui-icon-email","layui-icon-rss","layui-icon-wifi","layui-icon-logout","layui-icon-android","layui-icon-ios","layui-icon-windows","layui-icon-transfer","layui-icon-service","layui-icon-subtraction","layui-icon-addition","layui-icon-slider","layui-icon-print","layui-icon-export","layui-icon-cols","layui-icon-screen-restore","layui-icon-screen-full","layui-icon-rate-half","layui-icon-rate","layui-icon-rate-solid","layui-icon-cellphone","layui-icon-vercode","layui-icon-login-wechat","layui-icon-login-qq","layui-icon-login-weibo","layui-icon-password","layui-icon-username","layui-icon-refresh-3","layui-icon-auz","layui-icon-spread-left","layui-icon-shrink-right","layui-icon-snowflake","layui-icon-tips","layui-icon-note","layui-icon-home","layui-icon-senior","layui-icon-refresh","layui-icon-refresh-1","layui-icon-flag","layui-icon-theme","layui-icon-notice","layui-icon-website","layui-icon-console","layui-icon-face-surprised","layui-icon-set","layui-icon-template-1","layui-icon-app","layui-icon-template","layui-icon-praise","layui-icon-tread","layui-icon-male","layui-icon-female","layui-icon-camera","layui-icon-camera-fill","layui-icon-more","layui-icon-more-vertical","layui-icon-rmb","layui-icon-dollar","layui-icon-diamond","layui-icon-fire","layui-icon-return","layui-icon-location","layui-icon-read","layui-icon-survey","layui-icon-face-smile","layui-icon-face-cry","layui-icon-cart-simple","layui-icon-cart","layui-icon-next","layui-icon-prev","layui-icon-upload-drag","layui-icon-upload","layui-icon-download-circle","layui-icon-component","layui-icon-file-b","layui-icon-user","layui-icon-find-fill","layui-icon-loading","layui-icon-loading-1","layui-icon-add-1","layui-icon-play","layui-icon-pause","layui-icon-headset","layui-icon-video","layui-icon-voice","layui-icon-speaker","layui-icon-fonts-del","layui-icon-fonts-code","layui-icon-fonts-html","layui-icon-fonts-strong","layui-icon-unlink","layui-icon-picture","layui-icon-link","layui-icon-face-smile-b","layui-icon-align-left","layui-icon-align-right","layui-icon-align-center","layui-icon-fonts-u","layui-icon-fonts-i","layui-icon-tabs","layui-icon-radio","layui-icon-circle","layui-icon-edit","layui-icon-share","layui-icon-delete","layui-icon-form","layui-icon-cellphone-fine","layui-icon-dialogue","layui-icon-fonts-clear","layui-icon-layer","layui-icon-date","layui-icon-water","layui-icon-code-circle","layui-icon-carousel","layui-icon-prev-circle","layui-icon-layouts","layui-icon-util","layui-icon-templeate-1","layui-icon-upload-circle","layui-icon-tree","layui-icon-table","layui-icon-chart","layui-icon-chart-screen","layui-icon-engine","layui-icon-triangle-d","layui-icon-triangle-r","layui-icon-file","layui-icon-set-sm","layui-icon-reduce-circle","layui-icon-add-circle","layui-icon-404","layui-icon-about","layui-icon-up","layui-icon-down","layui-icon-left","layui-icon-right","layui-icon-circle-dot","layui-icon-search","layui-icon-set-fill","layui-icon-group","layui-icon-friends","layui-icon-reply-fill","layui-icon-menu-fill","layui-icon-log","layui-icon-picture-fine","layui-icon-face-smile-fine","layui-icon-list","layui-icon-release","layui-icon-ok","layui-icon-help","layui-icon-chat","layui-icon-top","layui-icon-star","layui-icon-star-fill","layui-icon-close-fill","layui-icon-close","layui-icon-ok-circle","layui-icon-add-circle-fine"]}}};return C.init(),new t},t.prototype.checkIcon=function(e,t){var i=n("*[lay-filter="+e+"]").next().find(".layui-iconpicker-item .layui-icon"),o=t;o.indexOf("#xe")>0?i.html(o):i.html("").attr("class",""+o)},e("iconPicker",new t)}),define("iconPicker",function(){}),function(e,t){"object"===("undefined"==typeof exports?"undefined":_typeof(exports))&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define("toastr",t):(e=e||self,e.notify=t())}(void 0,function(){function e(e,t){var n=document.createElement("div");for(var i in e){var o=e[i];"className"==i?(i="class",n.setAttribute(i,o)):"_"==i[0]&&n.addEventListener(i.slice(1),o)}if("string"==typeof t)n.innerHTML=t;else if("object"==_typeof(t)&&t.tagName)n.appendChild(t);else if(t)for(var r=0;r-1}function r(e,t){var n=e.className||"";if(o(n,t)){var i=n.split(/\s+/),r=i.indexOf(t);i.splice(r,1),e.className=i.join(" ")}""===e.className&&e.removeAttribute("class")}function a(e,t){var n={};for(var i in p)n[i]=p[i];for(var o=["bottomRight","bottomLeft","topRight","topLeft","topCenter","bottomCenter","center"],r=0;r-1?n.position=a:n.msg=a:"boolean"==typeof a?n.showClose=a:"function"==typeof a?a.call(this):"number"==typeof a&&1!=r&&(n.duration=a))}return n.type=t,s(n)}function s(i){var o,a=i.type,s=i.duration,u=i.msg,h=i.position,p=i.showClose,g=i.onClose,b=d();document.getElementsByClassName(h)[0]?o=document.getElementsByClassName(h)[0]:(o=e({className:"notify-msg-stage "+h}),m.push(o)),"loading"===a&&(u=""===u?"正在加载,请稍后":u,p=!1);var C;C=-1!=f.indexOf(h)?e({className:"notify-msg-wrapper"},[e({className:"notify-msg notify-bottom notify-msg-fade-in-b "+a},[e({className:"notify-msg-icon"},b[a]),e({className:"notify-msg-content"},u),e({className:"notify-msg-wait "+(p?"notify-msg-pointer":""),_click:function(){p&&(v=!0,y=!1,c(C,g,o))}},l(p))])]):e({className:"notify-msg-wrapper"},[e({className:"notify-msg notify-msg-fade-in "+a},[e({className:"notify-msg-icon"},b[a]),e({className:"notify-msg-content"},u),e({className:"notify-msg-wait "+(p?"notify-msg-pointer":""),_click:function(){p&&(v=!0,y=!1,c(C,g,o))}},l(p))])]);var x=C.querySelector(".notify-msg__circle");if(x&&(n(x,{animation:"notify-msg_"+a+" "+s+"ms linear"}),"onanimationend"in window?t(x,function(){c(C,g,o)}):setTimeout(function(){c(C,g,o)},s)),"loading"!=a&&setTimeout(function(){c(C,g,o)},s),o.children.length||document.body.appendChild(o),o.appendChild(C),n(C,{height:C.offsetHeight+"px"}),setTimeout(function(){-1!=f.indexOf(h)?r(C.children[0],"notify-msg-fade-in-b"):r(C.children[0],"notify-msg-fade-in")},300),"loading"==a)return function(){c(C,g,o)}}function l(e){if(e)return'\n \n '}function c(e,t,o){e&&(n(e,{padding:0,height:0}),$(e.children[0]).hasClass("notify-bottom")?i(e.children[0],"notify-msg-fade-out-b"):i(e.children[0],"notify-msg-fade-out"),v?(v=!1,t&&t()):y?t&&t():y=!0,setTimeout(function(){if(e){var t=!1;if(o){for(var n=0;n\n ',success:'\n \n ',warning:'\n \n ',error:'\n \n ',loading:'\n
        \n \n \n \n
        \n '}}function u(e){e&&e.parentNode.removeChild(e)}function h(){for(var e=0;e',r
        ':'
        ');var a=n[r].number;a||(a=r+1),o+=r==i?'
        '+a+"
        ":r':'
        '+a+"
        ";var s=n[r].title,l=n[r].desc;(s||l)&&(o+='
        ',s&&(o+='
        '+s+"
        "),l&&(o+='
        '+l+"
        "),o+="
        "),o+=""}o+="",t(e).prepend(o);var c=100/n.length;t(".step-item").css("width",c+"%")},o={render:function(e){e.indicator="none",e.arrow="always",e.autoplay=!1,e.stepWidth||(e.stepWidth="400px"),n.render(e);var o=e.stepItems;i(e.elem,o,0),t(".lay-step").css("width",e.stepWidth),n.on("change("+e.filter+")",function(n){t(e.elem).find(".lay-step").remove(),i(e.elem,o,n.index),t(".lay-step").css("width",e.stepWidth)}),t(e.elem).find(".layui-carousel-arrow").css("display","none"),t(e.elem).css("background-color","transparent")},next:function(e){t(e).find(".layui-carousel-arrow[lay-type=add]").trigger("click")},pre:function(e){t(e).find(".layui-carousel-arrow[lay-type=sub]").trigger("click")}};layui.link("/static/plugins/lay-module/step-lay/step.css"),e("step",o)}),define("step-lay",function(){});var _createClass=function(){function e(e,t){for(var n=0;n'+e+""+(this.options.permanentData.includes(e)?"":'×')+""}},{key:"onChange",value:function(e,t){this.options.onChange&&"function"==typeof this.options.onChange&&this.options.onChange(this.copyData,e,t)}},{key:"getData",value:function(){return this.copyData}},{key:"clearData",value:function(){this.options.data=[],this.elem.prevAll("span.layui-tag").remove()}}]),t}();return{render:function(e){return new t(e)}}})}("function"==typeof define&&define.amd?define:function(e,t){"undefined"!=typeof module&&module.exports?module.exports=t(require("jquery")):window.layui&&layui.define?layui.define("jquery",function(e){e("inputTag",t(layui.jquery))}):window.inputTag=t(window.jQuery)}),layui.link("/static/plugins/lay-module/inputTags/inputTags.css?v=7");var ChineseDistricts={886:{11e4:"北京市",12e4:"天津市",13e4:"河北省",14e4:"山西省",15e4:"内蒙古自治区",21e4:"辽宁省",22e4:"吉林省",23e4:"黑龙江省",31e4:"上海市",32e4:"江苏省",33e4:"浙江省",34e4:"安徽省",35e4:"福建省",36e4:"江西省",37e4:"山东省",41e4:"河南省",42e4:"湖北省",43e4:"湖南省",44e4:"广东省",45e4:"广西壮族自治区",46e4:"海南省",5e5:"重庆市",51e4:"四川省",52e4:"贵州省",53e4:"云南省",54e4:"西藏自治区",61e4:"陕西省",62e4:"甘肃省",63e4:"青海省",64e4:"宁夏回族自治区",65e4:"新疆维吾尔自治区",83e4:"台湾省",81e4:"香港特别行政区",82e4:"澳门特别行政区"},86:{"A-G":[{code:"340000",address:"安徽省"},{code:"110000",address:"北京市"},{code:"500000",address:"重庆市"},{code:"350000",address:"福建省"},{code:"620000",address:"甘肃省"},{code:"440000",address:"广东省"},{code:"450000",address:"广西壮族自治区"},{code:"520000",address:"贵州省"},{code:"820000",address:"澳门特别行政区"}],"H-K":[{code:"460000",address:"海南省"},{code:"130000",address:"河北省"},{code:"230000",address:"黑龙江省"},{code:"410000",address:"河南省"},{code:"420000",address:"湖北省"},{code:"430000",address:"湖南省"},{code:"320000",address:"江苏省"},{code:"360000",address:"江西省"},{code:"220000",address:"吉林省"}],"L-S":[{code:"210000",address:"辽宁省"},{code:"150000",address:"内蒙古自治区"},{code:"640000",address:"宁夏回族自治区"},{code:"630000",address:"青海省"},{code:"370000",address:"山东省"},{code:"310000",address:"上海市"},{code:"140000",address:"山西省"},{code:"610000",address:"陕西省"},{code:"510000",address:"四川省"}],"T-Z":[{code:"120000",address:"天津市"},{code:"830000",address:"台湾省"},{code:"650000",address:"新疆维吾尔自治区"},{code:"540000",address:"西藏自治区"},{code:"810000",address:"香港特别行政区"},{code:"530000",address:"云南省"},{code:"330000",address:"浙江省"}]},11e4:{110100:"北京市"},110100:{110101:"东城区",110102:"西城区",110103:"崇文区",110104:"宣武区",110105:"朝阳区",110106:"丰台区",110107:"石景山区",110108:"海淀区",110109:"门头沟区",110111:"房山区",110112:"通州区",110113:"顺义区",110114:"昌平区",110115:"大兴区",110116:"怀柔区",110117:"平谷区",110228:"密云县",110229:"延庆县"},12e4:{120100:"天津市 "},120100:{120101:"和平区",120102:"河东区",120103:"河西区",120104:"南开区",120105:"河北区",120106:"红桥区",120107:"塘沽区",120108:"汉沽区",120109:"大港区",120110:"东丽区",120111:"西青区",120112:"津南区",120113:"北辰区",120114:"武清区",120115:"宝坻区",120116:"滨海新区",120221:"宁河县",120223:"静海县",120225:"蓟县"},13e4:{130100:"石家庄市",130200:"唐山市",130300:"秦皇岛市",130400:"邯郸市",130500:"邢台市",130600:"保定市",130700:"张家口市",130800:"承德市",130900:"沧州市",131e3:"廊坊市",131100:"衡水市"},130100:{130102:"长安区",130103:"桥东区",130104:"桥西区",130105:"新华区",130107:"井陉矿区",130108:"裕华区",130121:"井陉县",130123:"正定县",130124:"栾城区",130125:"行唐县",130126:"灵寿县",130127:"高邑县",130128:"深泽县",130129:"赞皇县",130130:"无极县",130131:"平山县",130132:"元氏县",130133:"赵县",130181:"辛集市",130182:"藁城区",130183:"晋州市",130184:"新乐市",130185:"鹿泉区"},130200:{130202:"路南区",130203:"路北区",130204:"古冶区",130205:"开平区",130207:"丰南区",130208:"丰润区",130223:"滦县",130224:"滦南县",130225:"乐亭县",130227:"迁西县",130229:"玉田县",130230:"曹妃甸区",130281:"遵化市",130283:"迁安市",130299:"唐海县"},130300:{130302:"海港区",130303:"山海关区",130304:"北戴河区",130321:"青龙满族自治县",130322:"昌黎县",130323:"抚宁县",130324:"卢龙县",130399:"经济技术开发区"},130400:{130402:"邯山区",130403:"丛台区",130404:"复兴区",130406:"峰峰矿区",130421:"邯郸县",130423:"临漳县",130424:"成安县",130425:"大名县",130426:"涉县",130427:"磁县",130428:"肥乡县",130429:"永年县",130430:"邱县",130431:"鸡泽县",130432:"广平县",130433:"馆陶县",130434:"魏县",130435:"曲周县",130481:"武安市"},130500:{130502:"桥东区",130503:"桥西区",130521:"邢台县",130522:"临城县",130523:"内丘县",130524:"柏乡县",130525:"隆尧县",130526:"任县",130527:"南和县",130528:"宁晋县",130529:"巨鹿县",130530:"新河县",130531:"广宗县",130532:"平乡县",130533:"威县",130534:"清河县",130535:"临西县",130581:"南宫市",130582:"沙河市"},130600:{130602:"新市区",130603:"北市区",130604:"南市区",130621:"满城区",130622:"清苑区",130623:"涞水县",130624:"阜平县",130625:"徐水区",130626:"定兴县",130627:"唐县",130628:"高阳县",130629:"容城县",130630:"涞源县",130631:"望都县",130632:"安新县",130633:"易县",130634:"曲阳县",130635:"蠡县",130636:"顺平县",130637:"博野县",130638:"雄县",130681:"涿州市",130682:"定州市",130683:"安国市",130684:"高碑店市",130698:"高开区"},130700:{130702:"桥东区",130703:"桥西区",130705:"宣化区",130706:"下花园区",130721:"宣化县",130722:"张北县",130723:"康保县",130724:"沽源县",130725:"尚义县",130726:"蔚县",130727:"阳原县",130728:"怀安县",130729:"万全县",130730:"怀来县",130731:"涿鹿县",130732:"赤城县",130733:"崇礼县"},130800:{130802:"双桥区",130803:"双滦区",130804:"鹰手营子矿区",130821:"承德县",130822:"兴隆县",130823:"平泉县",130824:"滦平县",130825:"隆化县",130826:"丰宁满族自治县",130827:"宽城满族自治县",130828:"围场满族蒙古族自治县"},130900:{130902:"新华区",130903:"运河区",130921:"沧县",130922:"青县",130923:"东光县",130924:"海兴县",130925:"盐山县",130926:"肃宁县",130927:"南皮县",130928:"吴桥县",130929:"献县",130930:"孟村回族自治县",130981:"泊头市",130982:"任丘市",130983:"黄骅市",130984:"河间市"},131e3:{131002:"安次区",131003:"广阳区",131022:"固安县",131023:"永清县",131024:"香河县",131025:"大城县",131026:"文安县",131028:"大厂回族自治县",131051:"开发区",131052:"燕郊经济技术开发区",131081:"霸州市",131082:"三河市",131099:"廊坊经济技术开发区"},131100:{131102:"桃城区",131121:"枣强县",131122:"武邑县",131123:"武强县",131124:"饶阳县",131125:"安平县",131126:"故城县",131127:"景县",131128:"阜城县",131181:"冀州市",131182:"深州市"},14e4:{140100:"太原市",140200:"大同市",140300:"阳泉市",140400:"长治市",140500:"晋城市",140600:"朔州市",140700:"晋中市",140800:"运城市",140900:"忻州市",141e3:"临汾市",141100:"吕梁市"},140100:{140105:"小店区",140106:"迎泽区",140107:"杏花岭区",140108:"尖草坪区",140109:"万柏林区",140110:"晋源区",140121:"清徐县",140122:"阳曲县",140123:"娄烦县",140181:"古交市"},140200:{140202:"城区",140203:"矿区",140211:"南郊区",140212:"新荣区",140221:"阳高县",140222:"天镇县",140223:"广灵县",140224:"灵丘县",140225:"浑源县",140226:"左云县",140227:"大同县"},140300:{140302:"城区",140303:"矿区",140311:"郊区",140321:"平定县",140322:"盂县"},140400:{140421:"长治县",140423:"襄垣县",140424:"屯留县",140425:"平顺县",140426:"黎城县",140427:"壶关县",140428:"长子县",140429:"武乡县",140430:"沁县",140431:"沁源县",140481:"潞城市",140482:"城区",140483:"郊区",140484:"高新区"},140500:{140502:"城区",140521:"沁水县",140522:"阳城县",140524:"陵川县",140525:"泽州县",140581:"高平市"},140600:{140602:"朔城区",140603:"平鲁区",140621:"山阴县",140622:"应县",140623:"右玉县",140624:"怀仁县"},140700:{140702:"榆次区",140721:"榆社县",140722:"左权县",140723:"和顺县",140724:"昔阳县",140725:"寿阳县",140726:"太谷县",140727:"祁县",140728:"平遥县",140729:"灵石县",140781:"介休市"},140800:{140802:"盐湖区",140803:"风陵渡经济开发区",140821:"临猗县",140822:"万荣县",140823:"闻喜县",140824:"稷山县",140825:"新绛县",140826:"绛县",140827:"垣曲县",140828:"夏县",140829:"平陆县",140830:"芮城县",140881:"永济市",140882:"河津市"},140900:{140902:"忻府区",140921:"定襄县",140922:"五台县",140923:"代县",140924:"繁峙县",140925:"宁武县",140926:"静乐县",140927:"神池县",140928:"五寨县",140929:"岢岚县",140930:"河曲县",140931:"保德县",140932:"偏关县",140981:"原平市"},141e3:{141002:"尧都区",141021:"曲沃县",141022:"翼城县",141023:"襄汾县",141024:"洪洞县",141025:"古县",141026:"安泽县",141027:"浮山县",141028:"吉县",141029:"乡宁县",141030:"大宁县",141031:"隰县",141032:"永和县",141033:"蒲县",141034:"汾西县",141081:"侯马市",141082:"霍州市"},141100:{141102:"离石区",141121:"文水县",141122:"交城县",141123:"兴县",141124:"临县",141125:"柳林县",141126:"石楼县",141127:"岚县",141128:"方山县",141129:"中阳县",141130:"交口县",141181:"孝义市",141182:"汾阳市"},15e4:{150100:"呼和浩特市",150200:"包头市",150300:"乌海市",150400:"赤峰市",150500:"通辽市",150600:"鄂尔多斯市",150700:"呼伦贝尔市",150800:"巴彦淖尔市",150900:"乌兰察布市",152200:"兴安盟",152500:"锡林郭勒盟",152900:"阿拉善盟"},150100:{150102:"新城区",150103:"回民区",150104:"玉泉区",150105:"赛罕区",150121:"土默特左旗",150122:"托克托县",150123:"和林格尔县",150124:"清水河县",150125:"武川县"},150200:{150202:"东河区",150203:"昆都仑区",150204:"青山区",150205:"石拐区",150206:"白云鄂博矿区",150207:"九原区",150221:"土默特右旗",150222:"固阳县",150223:"达尔罕茂明安联合旗"},150300:{150302:"海勃湾区",150303:"海南区",150304:"乌达区"},150400:{150402:"红山区",150403:"元宝山区",150404:"松山区",150421:"阿鲁科尔沁旗",150422:"巴林左旗",150423:"巴林右旗",150424:"林西县",150425:"克什克腾旗",150426:"翁牛特旗",150428:"喀喇沁旗",150429:"宁城县",150430:"敖汉旗"},150500:{150502:"科尔沁区",150521:"科尔沁左翼中旗",150522:"科尔沁左翼后旗",150523:"开鲁县",150524:"库伦旗",150525:"奈曼旗",150526:"扎鲁特旗",150581:"霍林郭勒市"},150600:{150602:"东胜区",150621:"达拉特旗",150622:"准格尔旗",150623:"鄂托克前旗",150624:"鄂托克旗",150625:"杭锦旗",150626:"乌审旗",150627:"伊金霍洛旗"},150700:{150702:"海拉尔区",150703:"扎赉诺尔区",150721:"阿荣旗",150722:"莫力达瓦达斡尔族自治旗",150723:"鄂伦春自治旗",150724:"鄂温克族自治旗",150725:"陈巴尔虎旗",150726:"新巴尔虎左旗",150727:"新巴尔虎右旗",150781:"满洲里市",150782:"牙克石市",150783:"扎兰屯市",150784:"额尔古纳市",150785:"根河市"},150800:{150802:"临河区",150821:"五原县",150822:"磴口县",150823:"乌拉特前旗",150824:"乌拉特中旗",150825:"乌拉特后旗",150826:"杭锦后旗"},150900:{150902:"集宁区",150921:"卓资县",150922:"化德县",150923:"商都县",150924:"兴和县",150925:"凉城县",150926:"察哈尔右翼前旗",150927:"察哈尔右翼中旗",150928:"察哈尔右翼后旗",150929:"四子王旗",150981:"丰镇市"},152200:{152201:"乌兰浩特市",152202:"阿尔山市",152221:"科尔沁右翼前旗",152222:"科尔沁右翼中旗",152223:"扎赉特旗",152224:"突泉县"},152500:{152501:"二连浩特市",152502:"锡林浩特市",152522:"阿巴嘎旗",152523:"苏尼特左旗",152524:"苏尼特右旗",152525:"东乌珠穆沁旗",152526:"西乌珠穆沁旗",152527:"太仆寺旗",152528:"镶黄旗",152529:"正镶白旗",152530:"正蓝旗",152531:"多伦县"},152900:{152921:"阿拉善左旗",152922:"阿拉善右旗",152923:"额济纳旗"},21e4:{210100:"沈阳市",210200:"大连市",210300:"鞍山市",210400:"抚顺市",210500:"本溪市",210600:"丹东市",210700:"锦州市",210800:"营口市",210900:"阜新市",211e3:"辽阳市",211100:"盘锦市",211200:"铁岭市",211300:"朝阳市",211400:"葫芦岛市"},210100:{210102:"和平区",210103:"沈河区",210104:"大东区",210105:"皇姑区",210106:"铁西区",210111:"苏家屯区",210112:"东陵区",210113:"新城子区",210114:"于洪区",210122:"辽中县",210123:"康平县",210124:"法库县",210181:"新民市",210182:"浑南区",210183:"张士开发区",210184:"沈北新区"},210200:{210202:"中山区",210203:"西岗区",210204:"沙河口区",210211:"甘井子区",210212:"旅顺口区",210213:"金州区",210214:"大连经济技术开发区",210224:"长海县",210251:"开发区",210281:"瓦房店市",210282:"普兰店市",210283:"庄河市",210297:"岭前区"},210300:{210302:"铁东区",210303:"铁西区",210304:"立山区",210311:"千山区",210321:"台安县",210323:"岫岩满族自治县",210351:"高新区",210381:"海城市"},210400:{210402:"新抚区",210403:"东洲区",210404:"望花区",210411:"顺城区",210421:"抚顺县",210422:"新宾满族自治县",210423:"清原满族自治县"},210500:{210502:"平山区",210503:"溪湖区",210504:"明山区",210505:"南芬区",210521:"本溪满族自治县",210522:"桓仁满族自治县"},210600:{210602:"元宝区",210603:"振兴区",210604:"振安区",210624:"宽甸满族自治县",210681:"东港市",210682:"凤城市"},210700:{210702:"古塔区",210703:"凌河区",210711:"太和区",210726:"黑山县",210727:"义县",210781:"凌海市",210782:"北镇市"},210800:{210802:"站前区",210803:"西市区",210804:"鲅鱼圈区",210811:"老边区",210881:"盖州市",210882:"大石桥市"},210900:{210902:"海州区",210903:"新邱区",210904:"太平区",210905:"清河门区",210911:"细河区",210921:"阜新蒙古族自治县",210922:"彰武县"},211e3:{211002:"白塔区",211003:"文圣区",211004:"宏伟区",211005:"弓长岭区",211011:"太子河区",211021:"辽阳县",211081:"灯塔市"},211100:{211102:"双台子区",211103:"兴隆台区",211121:"大洼县",211122:"盘山县"},211200:{211202:"银州区",211204:"清河区",211221:"铁岭县",211223:"西丰县",211224:"昌图县",211281:"调兵山市",211282:"开原市"},211300:{211302:"双塔区",211303:"龙城区",211321:"朝阳县",211322:"建平县",211324:"喀喇沁左翼蒙古族自治县",211381:"北票市",211382:"凌源市"},211400:{211402:"连山区",211403:"龙港区",211404:"南票区",211421:"绥中县",211422:"建昌县",211481:"兴城市"},22e4:{220100:"长春市",220200:"吉林市",220300:"四平市",220400:"辽源市",220500:"通化市",220600:"白山市",220700:"松原市",220800:"白城市",222400:"延边朝鲜族自治州"},220100:{220102:"南关区",220103:"宽城区",220104:"朝阳区",220105:"二道区",220106:"绿园区",220112:"双阳区",220117:"高新北区",220119:"高新区",220122:"农安县",220181:"九台区",220182:"榆树市",220183:"德惠市",220184:"高新技术产业开发区",220185:"汽车产业开发区",220186:"经济技术开发区",220187:"净月旅游开发区"},220200:{220202:"昌邑区",220203:"龙潭区",220204:"船营区",220211:"丰满区",220221:"永吉县",220281:"蛟河市",220282:"桦甸市",220283:"舒兰市",220284:"磐石市"},220300:{220302:"铁西区",220303:"铁东区",220322:"梨树县",220323:"伊通满族自治县",220381:"公主岭市",220382:"双辽市"},220400:{220402:"龙山区",220403:"西安区",220421:"东丰县",220422:"东辽县"},220500:{220502:"东昌区",220503:"二道江区",220521:"通化县",220523:"辉南县",220524:"柳河县",220581:"梅河口市",220582:"集安市"},220600:{220602:"浑江区",220603:"八道江区",220621:"抚松县",220622:"靖宇县",220623:"长白朝鲜族自治县",220625:"江源区",220681:"临江市"},220700:{220702:"宁江区",220721:"前郭尔罗斯蒙古族自治县",220722:"长岭县",220723:"乾安县",220781:"扶余市"},220800:{220802:"洮北区",220821:"镇赉县",220822:"通榆县",220881:"洮南市",220882:"大安市"},222400:{222401:"延吉市",222402:"图们市",222403:"敦化市",222404:"珲春市",222405:"龙井市",222406:"和龙市",222424:"汪清县",222426:"安图县"},23e4:{230100:"哈尔滨市",230200:"齐齐哈尔市",230300:"鸡西市",230400:"鹤岗市",230500:"双鸭山市",230600:"大庆市",230700:"伊春市",230800:"佳木斯市",230900:"七台河市",231e3:"牡丹江市",231100:"黑河市",231200:"绥化市",232700:"大兴安岭地区"},230100:{230102:"道里区",230103:"南岗区",230104:"道外区",230106:"香坊区",230107:"动力区",230108:"平房区",230109:"松北区",230110:"香坊区",230111:"呼兰区",230123:"依兰县",230124:"方正县",230125:"宾县",230126:"巴彦县",230127:"木兰县",230128:"通河县",230129:"延寿县",230181:"阿城区",230182:"双城区",230183:"尚志市",230184:"五常市",230185:"阿城市"},230200:{230202:"龙沙区",230203:"建华区",230204:"铁锋区",230205:"昂昂溪区",230206:"富拉尔基区",230207:"碾子山区",230208:"梅里斯达斡尔族区",230221:"龙江县",230223:"依安县",230224:"泰来县",230225:"甘南县",230227:"富裕县",230229:"克山县",230230:"克东县",230231:"拜泉县",230281:"讷河市"},230300:{230302:"鸡冠区",230303:"恒山区",230304:"滴道区",230305:"梨树区",230306:"城子河区",230307:"麻山区",230321:"鸡东县",230381:"虎林市",230382:"密山市"},230400:{230402:"向阳区",230403:"工农区",230404:"南山区",230405:"兴安区",230406:"东山区",230407:"兴山区",230421:"萝北县",230422:"绥滨县"},230500:{230502:"尖山区",230503:"岭东区",230505:"四方台区",230506:"宝山区",230521:"集贤县",230522:"友谊县",230523:"宝清县",230524:"饶河县"},230600:{230602:"萨尔图区",230603:"龙凤区",230604:"让胡路区",230605:"红岗区",230606:"大同区",230621:"肇州县",230622:"肇源县",230623:"林甸县",230624:"杜尔伯特蒙古族自治县"},230700:{230702:"伊春区",230703:"南岔区",230704:"友好区",230705:"西林区",230706:"翠峦区",230707:"新青区",230708:"美溪区",230709:"金山屯区",230710:"五营区",230711:"乌马河区",230712:"汤旺河区",230713:"带岭区",230714:"乌伊岭区",230715:"红星区",230716:"上甘岭区",230722:"嘉荫县",230781:"铁力市"},230800:{230802:"永红区",230803:"向阳区",230804:"前进区",230805:"东风区",230811:"郊区",230822:"桦南县",230826:"桦川县",230828:"汤原县",230833:"抚远县",230881:"同江市",230882:"富锦市"},230900:{230902:"新兴区",230903:"桃山区",230904:"茄子河区",230921:"勃利县"},231e3:{231002:"东安区",231003:"阳明区",231004:"爱民区",231005:"西安区",231024:"东宁县",231025:"林口县",231081:"绥芬河市",231083:"海林市",231084:"宁安市",231085:"穆棱市"},231100:{231102:"爱辉区",231121:"嫩江县",231123:"逊克县",231124:"孙吴县",231181:"北安市",231182:"五大连池市"},231200:{231202:"北林区",231221:"望奎县",231222:"兰西县",231223:"青冈县",231224:"庆安县",231225:"明水县",231226:"绥棱县",231281:"安达市",231282:"肇东市",231283:"海伦市"},232700:{232702:"松岭区",232703:"新林区",232704:"呼中区",232721:"呼玛县",232722:"塔河县",232723:"漠河县",232724:"加格达奇区"},31e4:{310100:"上海市"},310100:{310101:"黄浦区",310103:"卢湾区",310104:"徐汇区",310105:"长宁区",310106:"静安区",310107:"普陀区",310108:"闸北区",310109:"虹口区",310110:"杨浦区",310112:"闵行区",310113:"宝山区",310114:"嘉定区",310115:"浦东新区",310116:"金山区",310117:"松江区",310118:"青浦区",310119:"南汇区",310120:"奉贤区",310152:"川沙区",310230:"崇明县"},32e4:{320100:"南京市",320200:"无锡市",320300:"徐州市",320400:"常州市",320500:"苏州市",320600:"南通市",320700:"连云港市",320800:"淮安市",320900:"盐城市",321e3:"扬州市",321100:"镇江市",321200:"泰州市",321300:"宿迁市"},320100:{320102:"玄武区",320103:"白下区",320104:"秦淮区",320105:"建邺区",320106:"鼓楼区",320107:"下关区",320111:"浦口区",320113:"栖霞区",320114:"雨花台区",320115:"江宁区",320116:"六合区",320124:"溧水区",320125:"高淳区"},320200:{320202:"崇安区",320203:"南长区",320204:"北塘区",320205:"锡山区",320206:"惠山区",320211:"滨湖区",320213:"梁溪区",320214:"新吴区",320281:"江阴市",320282:"宜兴市",320296:"新区"},320300:{320302:"鼓楼区",320303:"云龙区",320304:"九里区",320305:"贾汪区",320311:"泉山区",320321:"丰县",320322:"沛县",320323:"铜山区",320324:"睢宁县",320381:"新沂市",320382:"邳州市"},320400:{320402:"天宁区",320404:"钟楼区",320405:"戚墅堰区",320411:"新北区",320412:"武进区",320481:"溧阳市",320482:"金坛区"},320500:{320502:"沧浪区",320503:"平江区",320504:"金阊区",320505:"虎丘区",320506:"吴中区",320507:"相城区",320508:"姑苏区",320510:"新区",320581:"常熟市",320582:"张家港市",320583:"昆山市",320584:"吴江区",320585:"太仓市",320595:"园区"},320600:{320602:"崇川区",320611:"港闸区",320612:"通州区",320613:"开发区",320621:"海安县",320623:"如东县",320681:"启东市",320682:"如皋市",320683:"通州市",320684:"海门市",320693:"开发区"},320700:{320703:"连云区",320705:"新浦区",320706:"海州区",320721:"赣榆区",320722:"东海县",320723:"灌云县",320724:"灌南县"},320800:{320802:"清河区",320803:"淮安区",320804:"淮阴区",320809:"开发区",320811:"清浦区",320826:"涟水县",320829:"洪泽县",320830:"盱眙县",320831:"金湖县"},320900:{320902:"亭湖区",320903:"盐都区",320921:"响水县",320922:"滨海县",320923:"阜宁县",320924:"射阳县",320925:"建湖县",320981:"东台市",320982:"大丰市"},321e3:{321002:"广陵区",321003:"邗江区",321011:"维扬区",321023:"宝应县",321081:"仪征市",321084:"高邮市",321088:"江都区",321092:"经济开发区"},321100:{321102:"京口区",321111:"润州区",321112:"丹徒区",321181:"丹阳市",321182:"扬中市",321183:"句容市"},321200:{321202:"海陵区",321203:"高港区",321281:"兴化市",321282:"靖江市",321283:"泰兴市",321284:"姜堰区"},321300:{321302:"宿城区",321311:"宿豫区",321322:"沭阳县",321323:"泗阳县",321324:"泗洪县"},33e4:{330100:"杭州市",330200:"宁波市",330300:"温州市",330400:"嘉兴市",330500:"湖州市",330600:"绍兴市",330700:"金华市",330800:"衢州市",330900:"舟山市",331e3:"台州市",331100:"丽水市"},330100:{330102:"上城区",330103:"下城区",330104:"江干区",330105:"拱墅区",330106:"西湖区",330108:"滨江区",330109:"萧山区",330110:"余杭区",330122:"桐庐县",330127:"淳安县",330182:"建德市",330183:"富阳区",330185:"临安市"},330200:{330203:"海曙区",330204:"江东区",330205:"江北区",330206:"北仑区",330211:"镇海区",330212:"鄞州区",330225:"象山县",330226:"宁海县",330281:"余姚市",330282:"慈溪市",330283:"奉化市",330299:"高新区"},330300:{330302:"鹿城区",330303:"龙湾区",330304:"瓯海区",330322:"洞头县",330324:"永嘉县",330326:"平阳县",330327:"苍南县",330328:"文成县",330329:"泰顺县",330381:"瑞安市",330382:"乐清市"},330400:{330402:"南湖区",330411:"秀洲区",330421:"嘉善县",330424:"海盐县",330481:"海宁市",330482:"平湖市",330483:"桐乡市"},330500:{330502:"吴兴区",330503:"南浔区",330521:"德清县",330522:"长兴县",330523:"安吉县"},330600:{330602:"越城区",330621:"柯桥区",330624:"新昌县",330681:"诸暨市",330682:"上虞区",330683:"嵊州市"},330700:{330702:"婺城区",330703:"金东区",330723:"武义县",330726:"浦江县",330727:"磐安县",330781:"兰溪市",330782:"义乌市",330783:"东阳市",330784:"永康市"},330800:{330802:"柯城区",330803:"衢江区",330822:"常山县",330824:"开化县",330825:"龙游县",330881:"江山市"},330900:{330902:"定海区",330903:"普陀区",330921:"岱山县",330922:"嵊泗县"},331e3:{331002:"椒江区",331003:"黄岩区",331004:"路桥区",331021:"玉环县",331022:"三门县",331023:"天台县",331024:"仙居县",331081:"温岭市",331082:"临海市"},331100:{331102:"莲都区",331121:"青田县",331122:"缙云县",331123:"遂昌县",331124:"松阳县",331125:"云和县",331126:"庆元县",331127:"景宁畲族自治县",331181:"龙泉市"},34e4:{340100:"合肥市",340200:"芜湖市",340300:"蚌埠市",340400:"淮南市",340500:"马鞍山市",340600:"淮北市",340700:"铜陵市",340800:"安庆市",341e3:"黄山市",341100:"滁州市",341200:"阜阳市",341300:"宿州市",341500:"六安市",341600:"亳州市",341700:"池州市",341800:"宣城市"},340100:{340102:"瑶海区",340103:"庐阳区",340104:"蜀山区",340111:"包河区",340121:"长丰县",340122:"肥东县",340123:"肥西县",340151:"高新区",340191:"中区",340198:"合肥经济技术开发区",340199:"合肥市政务新区",341400:"巢湖市",341402:"居巢区",341421:"庐江县"},340200:{340202:"镜湖区",340203:"弋江区",340207:"鸠江区",340208:"三山区",340221:"芜湖县",340222:"繁昌县",340223:"南陵县",341422:"无为县"},340300:{340302:"龙子湖区",340303:"蚌山区",340304:"禹会区",340311:"淮上区",340321:"怀远县",340322:"五河县",340323:"固镇县"},340400:{340402:"大通区",340403:"田家庵区",340404:"谢家集区",340405:"八公山区",340406:"潘集区",340421:"凤台县",341521:"寿县"},340500:{340502:"金家庄区",340503:"花山区",340504:"雨山区",340506:"博望区",340521:"当涂县",341423:"含山县",341424:"和县"},340600:{340602:"杜集区",340603:"相山区",340604:"烈山区",340621:"濉溪县"},340700:{340702:"铜官山区",340703:"狮子山区",340711:"郊区",340721:"义安区",340823:"枞阳县"},340800:{340802:"迎江区",340803:"大观区",340811:"宜秀区",340822:"怀宁县",340823:"枞阳县",340824:"潜山县",340825:"太湖县",340826:"宿松县",340827:"望江县",340828:"岳西县",340881:"桐城市"},341e3:{341002:"屯溪区",341003:"黄山区",341004:"徽州区",341021:"歙县",341022:"休宁县",341023:"黟县",341024:"祁门县"},341100:{341102:"琅琊区",341103:"南谯区",341122:"来安县",341124:"全椒县",341125:"定远县",341126:"凤阳县",341181:"天长市",341182:"明光市"},341200:{341202:"颍州区",341203:"颍东区",341204:"颍泉区",341221:"临泉县",341222:"太和县",341225:"阜南县",341226:"颍上县",341282:"界首市"},341300:{341302:"埇桥区",341321:"砀山县",341322:"萧县",341323:"灵璧县",341324:"泗县"},341500:{341502:"金安区",341503:"裕安区",341522:"霍邱县",341523:"舒城县",341524:"金寨县",341525:"霍山县",341526:"叶集区"},341600:{341602:"谯城区",341621:"涡阳县",341622:"蒙城县",341623:"利辛县"},341700:{341702:"贵池区",341721:"东至县",341722:"石台县",341723:"青阳县"},341800:{341802:"宣州区",341821:"郎溪县",341822:"广德县",341823:"泾县",341824:"绩溪县",341825:"旌德县",341881:"宁国市"},35e4:{350100:"福州市",350200:"厦门市",350300:"莆田市",350400:"三明市",350500:"泉州市",350600:"漳州市",350700:"南平市",350800:"龙岩市",350900:"宁德市"},350100:{350102:"鼓楼区",350103:"台江区",350104:"仓山区",350105:"马尾区",350111:"晋安区",350121:"闽侯县",350122:"连江县",350123:"罗源县",350124:"闽清县",350125:"永泰县",350128:"平潭县",350181:"福清市",350182:"长乐市"},350200:{350203:"思明区",350205:"海沧区",350206:"湖里区",350211:"集美区",350212:"同安区",350213:"翔安区"},350300:{350302:"城厢区",350303:"涵江区",350304:"荔城区",350305:"秀屿区",350322:"仙游县"},350400:{350402:"梅列区",350403:"三元区",350421:"明溪县",350423:"清流县",350424:"宁化县",350425:"大田县",350426:"尤溪县",350427:"沙县",350428:"将乐县",350429:"泰宁县",350430:"建宁县",350481:"永安市"},350500:{350502:"鲤城区",350503:"丰泽区",350504:"洛江区",350505:"泉港区",350521:"惠安县",350524:"安溪县",350525:"永春县",350526:"德化县",350527:"金门县",350581:"石狮市",350582:"晋江市",350583:"南安市"},350600:{350602:"芗城区",350603:"龙文区",350622:"云霄县",350623:"漳浦县",350624:"诏安县",350625:"长泰县",350626:"东山县",350627:"南靖县",350628:"平和县",350629:"华安县",350681:"龙海市"},350700:{350702:"延平区",350721:"顺昌县",350722:"浦城县",350723:"光泽县",350724:"松溪县",350725:"政和县",350781:"邵武市",350782:"武夷山市",350783:"建瓯市",350784:"建阳区"},350800:{350802:"新罗区",350821:"长汀县",350822:"永定区",350823:"上杭县",350824:"武平县",350825:"连城县",350881:"漳平市"},350900:{350902:"蕉城区",350921:"霞浦县",350922:"古田县",350923:"屏南县",350924:"寿宁县",350925:"周宁县",350926:"柘荣县",350981:"福安市",350982:"福鼎市"},36e4:{360100:"南昌市",360200:"景德镇市",360300:"萍乡市",360400:"九江市",360500:"新余市",360600:"鹰潭市",360700:"赣州市",360800:"吉安市",360900:"宜春市",361e3:"抚州市",361100:"上饶市"},360100:{360102:"东湖区",360103:"西湖区",360104:"青云谱区",360105:"湾里区",360111:"青山湖区",360121:"南昌县",360122:"新建县",360123:"安义县",360124:"进贤县",360125:"红谷滩新区",360126:"经济技术开发区",360127:"昌北区"},360200:{360202:"昌江区",360203:"珠山区",360222:"浮梁县",360281:"乐平市"},360300:{360302:"安源区",360313:"湘东区",360321:"莲花县",360322:"上栗县",360323:"芦溪县"},360400:{360402:"庐山区",360403:"浔阳区",360421:"九江县",360423:"武宁县",360424:"修水县",360425:"永修县",360426:"德安县",360427:"星子县",360428:"都昌县",360429:"湖口县",360430:"彭泽县",360481:"瑞昌市",360483:"共青城市"},360500:{360502:"渝水区",360521:"分宜县"},360600:{360602:"月湖区",360622:"余江县",360681:"贵溪市"},360700:{360702:"章贡区",360704:"赣县",360722:"信丰县",360723:"大余县",360724:"上犹县",360725:"崇义县",360726:"安远县",360727:"龙南县",360728:"定南县",360729:"全南县",360730:"宁都县",360731:"于都县",360732:"兴国县",360733:"会昌县",360734:"寻乌县",360735:"石城县",360751:"黄金区",360781:"瑞金市",360782:"南康区"},360800:{360802:"吉州区",360803:"青原区",360821:"吉安县",360822:"吉水县",360823:"峡江县",360824:"新干县",360825:"永丰县",360826:"泰和县",360827:"遂川县",360828:"万安县",360829:"安福县",360830:"永新县",360881:"井冈山市"},360900:{360902:"袁州区",360921:"奉新县",360922:"万载县",360923:"上高县",360924:"宜丰县",360925:"靖安县",360926:"铜鼓县",360981:"丰城市",360982:"樟树市",360983:"高安市"},361e3:{361002:"临川区",361021:"南城县",361022:"黎川县",361023:"南丰县", -361024:"崇仁县",361025:"乐安县",361026:"宜黄县",361027:"金溪县",361028:"资溪县",361029:"东乡县",361030:"广昌县"},361100:{361102:"信州区",361121:"上饶县",361122:"广丰县",361123:"玉山县",361124:"铅山县",361125:"横峰县",361126:"弋阳县",361127:"余干县",361128:"鄱阳县",361129:"万年县",361130:"婺源县",361181:"德兴市"},37e4:{370100:"济南市",370200:"青岛市",370300:"淄博市",370400:"枣庄市",370500:"东营市",370600:"烟台市",370700:"潍坊市",370800:"济宁市",370900:"泰安市",371e3:"威海市",371100:"日照市",371200:"莱芜市",371300:"临沂市",371400:"德州市",371500:"聊城市",371600:"滨州市",371700:"菏泽市"},370100:{250101:"高新区",370102:"历下区",370103:"市中区",370104:"槐荫区",370105:"天桥区",370112:"历城区",370113:"长清区",370124:"平阴县",370125:"济阳县",370126:"商河县",370181:"章丘市"},370200:{370202:"市南区",370203:"市北区",370204:"西海岸新区",370205:"四方区",370211:"黄岛区",370212:"崂山区",370213:"李沧区",370214:"城阳区",370251:"开发区",370281:"胶州市",370282:"即墨市",370283:"平度市",370284:"胶南市",370285:"莱西市"},370300:{370302:"淄川区",370303:"张店区",370304:"博山区",370305:"临淄区",370306:"周村区",370321:"桓台县",370322:"高青县",370323:"沂源县"},370400:{370402:"市中区",370403:"薛城区",370404:"峄城区",370405:"台儿庄区",370406:"山亭区",370481:"滕州市"},370500:{370502:"东营区",370503:"河口区",370521:"垦利县",370522:"利津县",370523:"广饶县",370589:"西城区",370590:"东城区"},370600:{370602:"芝罘区",370611:"福山区",370612:"牟平区",370613:"莱山区",370634:"长岛县",370681:"龙口市",370682:"莱阳市",370683:"莱州市",370684:"蓬莱市",370685:"招远市",370686:"栖霞市",370687:"海阳市"},370700:{370702:"潍城区",370703:"寒亭区",370704:"坊子区",370705:"奎文区",370706:"滨海经济技术开发区",370724:"临朐县",370725:"昌乐县",370751:"开发区",370781:"青州市",370782:"诸城市",370783:"寿光市",370784:"安丘市",370785:"高密市",370786:"昌邑市"},370800:{370802:"市中区",370811:"任城区",370826:"微山县",370827:"鱼台县",370828:"金乡县",370829:"嘉祥县",370830:"汶上县",370831:"泗水县",370832:"梁山县",370881:"曲阜市",370882:"兖州区",370883:"邹城市"},370900:{370902:"泰山区",370903:"岱岳区",370921:"宁阳县",370923:"东平县",370982:"新泰市",370983:"肥城市"},371e3:{371002:"环翠区",371081:"文登区",371082:"荣成市",371083:"乳山市"},371100:{371102:"东港区",371103:"岚山区",371121:"五莲县",371122:"莒县"},371200:{371202:"莱城区",371203:"钢城区"},371300:{371302:"兰山区",371311:"罗庄区",371312:"河东区",371321:"沂南县",371322:"郯城县",371323:"沂水县",371324:"兰陵县",371325:"费县",371326:"平邑县",371327:"莒南县",371328:"蒙阴县",371329:"临沭县"},371400:{371402:"德城区",371421:"陵城区",371422:"宁津县",371423:"庆云县",371424:"临邑县",371425:"齐河县",371426:"平原县",371427:"夏津县",371428:"武城县",371451:"开发区",371481:"乐陵市",371482:"禹城市"},371500:{371502:"东昌府区",371521:"阳谷县",371522:"莘县",371523:"茌平县",371524:"东阿县",371525:"冠县",371526:"高唐县",371581:"临清市"},371600:{371602:"滨城区",371621:"惠民县",371622:"阳信县",371623:"无棣县",371624:"沾化区",371625:"博兴县",371626:"邹平县"},371700:{371702:"牡丹区",371721:"曹县",371722:"单县",371723:"成武县",371724:"巨野县",371725:"郓城县",371726:"鄄城县",371727:"定陶县",371728:"东明县"},41e4:{410100:"郑州市",410200:"开封市",410300:"洛阳市",410400:"平顶山市",410500:"安阳市",410600:"鹤壁市",410700:"新乡市",410800:"焦作市",410900:"濮阳市",411e3:"许昌市",411100:"漯河市",411200:"三门峡市",411300:"南阳市",411400:"商丘市",411500:"信阳市",411600:"周口市",411700:"驻马店市",419001:"济源市"},410100:{410102:"中原区",410103:"二七区",410104:"管城回族区",410105:"金水区",410106:"上街区",410108:"惠济区",410122:"中牟县",410181:"巩义市",410182:"荥阳市",410183:"新密市",410184:"新郑市",410185:"登封市",410186:"郑东新区",410187:"高新区",410193:"经济开发区"},410200:{410202:"龙亭区",410203:"顺河回族区",410204:"鼓楼区",410205:"禹王台区",410211:"金明区",410221:"杞县",410222:"通许县",410223:"尉氏县",410224:"祥符区",410225:"兰考县"},410300:{410302:"老城区",410303:"西工区",410304:"瀍河回族区",410305:"涧西区",410306:"吉利区",410307:"洛龙区",410322:"孟津县",410323:"新安县",410324:"栾川县",410325:"嵩县",410326:"汝阳县",410327:"宜阳县",410328:"洛宁县",410329:"伊川县",410381:"偃师市"},410400:{410402:"新华区",410403:"卫东区",410404:"石龙区",410411:"湛河区",410421:"宝丰县",410422:"叶县",410423:"鲁山县",410425:"郏县",410481:"舞钢市",410482:"汝州市"},410500:{410502:"文峰区",410503:"北关区",410505:"殷都区",410506:"龙安区",410522:"安阳县",410523:"汤阴县",410526:"滑县",410527:"内黄县",410581:"林州市"},410600:{410602:"鹤山区",410603:"山城区",410611:"淇滨区",410621:"浚县",410622:"淇县"},410700:{410702:"红旗区",410703:"卫滨区",410704:"凤泉区",410711:"牧野区",410721:"新乡县",410724:"获嘉县",410725:"原阳县",410726:"延津县",410727:"封丘县",410728:"长垣县",410781:"卫辉市",410782:"辉县市"},410800:{410802:"解放区",410803:"中站区",410804:"马村区",410811:"山阳区",410821:"修武县",410822:"博爱县",410823:"武陟县",410825:"温县",410882:"沁阳市",410883:"孟州市"},410900:{410902:"华龙区",410922:"清丰县",410923:"南乐县",410926:"范县",410927:"台前县",410928:"濮阳县"},411e3:{411002:"魏都区",411023:"许昌县",411024:"鄢陵县",411025:"襄城县",411081:"禹州市",411082:"长葛市"},411100:{411102:"源汇区",411103:"郾城区",411104:"召陵区",411121:"舞阳县",411122:"临颍县"},411200:{411202:"湖滨区",411221:"渑池县",411222:"陕州区",411224:"卢氏县",411281:"义马市",411282:"灵宝市"},411300:{411302:"宛城区",411303:"卧龙区",411321:"南召县",411322:"方城县",411323:"西峡县",411324:"镇平县",411325:"内乡县",411326:"淅川县",411327:"社旗县",411328:"唐河县",411329:"新野县",411330:"桐柏县",411381:"邓州市"},411400:{411402:"梁园区",411403:"睢阳区",411421:"民权县",411422:"睢县",411423:"宁陵县",411424:"柘城县",411425:"虞城县",411426:"夏邑县",411481:"永城市"},411500:{411502:"浉河区",411503:"平桥区",411521:"罗山县",411522:"光山县",411523:"新县",411524:"商城县",411525:"固始县",411526:"潢川县",411527:"淮滨县",411528:"息县"},411600:{411602:"川汇区",411621:"扶沟县",411622:"西华县",411623:"商水县",411624:"沈丘县",411625:"郸城县",411626:"淮阳县",411627:"太康县",411628:"鹿邑县",411681:"项城市"},411700:{411702:"驿城区",411721:"西平县",411722:"上蔡县",411723:"平舆县",411724:"正阳县",411725:"确山县",411726:"泌阳县",411727:"汝南县",411728:"遂平县",411729:"新蔡县"},42e4:{420100:"武汉市",420200:"黄石市",420300:"十堰市",420500:"宜昌市",420600:"襄阳市",420700:"鄂州市",420800:"荆门市",420900:"孝感市",421e3:"荆州市",421100:"黄冈市",421200:"咸宁市",421300:"随州市",422800:"恩施土家族苗族自治州",422900:"仙桃市",429005:"潜江市",429006:"天门市",429021:"神农架林区"},420100:{420102:"江岸区",420103:"江汉区",420104:"硚口区",420105:"汉阳区",420106:"武昌区",420107:"青山区",420111:"洪山区",420112:"东西湖区",420113:"汉南区",420114:"蔡甸区",420115:"江夏区",420116:"黄陂区",420117:"新洲区",420199:"武汉经济技术开发区"},420200:{420202:"黄石港区",420203:"西塞山区",420204:"下陆区",420205:"铁山区",420222:"阳新县",420281:"大冶市"},420300:{420302:"茅箭区",420303:"张湾区",420321:"郧阳区",420322:"郧西县",420323:"竹山县",420324:"竹溪县",420325:"房县",420381:"丹江口市",420382:"城区"},420500:{420502:"西陵区",420503:"伍家岗区",420504:"点军区",420505:"猇亭区",420506:"夷陵区",420525:"远安县",420526:"兴山县",420527:"秭归县",420528:"长阳土家族自治县",420529:"五峰土家族自治县",420551:"葛洲坝区",420552:"开发区",420581:"宜都市",420582:"当阳市",420583:"枝江市"},420600:{420602:"襄城区",420606:"樊城区",420607:"襄州区",420624:"南漳县",420625:"谷城县",420626:"保康县",420682:"老河口市",420683:"枣阳市",420684:"宜城市"},420700:{420702:"梁子湖区",420703:"华容区",420704:"鄂城区"},420800:{420802:"东宝区",420804:"掇刀区",420821:"京山县",420822:"沙洋县",420881:"钟祥市"},420900:{420902:"孝南区",420921:"孝昌县",420922:"大悟县",420923:"云梦县",420981:"应城市",420982:"安陆市",420984:"汉川市"},421e3:{421002:"沙市区",421003:"荆州区",421022:"公安县",421023:"监利县",421024:"江陵县",421081:"石首市",421083:"洪湖市",421087:"松滋市"},421100:{421102:"黄州区",421121:"团风县",421122:"红安县",421123:"罗田县",421124:"英山县",421125:"浠水县",421126:"蕲春县",421127:"黄梅县",421181:"麻城市",421182:"武穴市"},421200:{421202:"咸安区",421221:"嘉鱼县",421222:"通城县",421223:"崇阳县",421224:"通山县",421281:"赤壁市",421282:"温泉城区"},421300:{421303:"曾都区",421321:"随县",421381:"广水市"},422800:{422801:"恩施市",422802:"利川市",422822:"建始县",422823:"巴东县",422825:"宣恩县",422826:"咸丰县",422827:"来凤县",422828:"鹤峰县"},43e4:{430100:"长沙市",430200:"株洲市",430300:"湘潭市",430400:"衡阳市",430500:"邵阳市",430600:"岳阳市",430700:"常德市",430800:"张家界市",430900:"益阳市",431e3:"郴州市",431100:"永州市",431200:"怀化市",431300:"娄底市",433100:"湘西土家族苗族自治州"},430100:{430102:"芙蓉区",430103:"天心区",430104:"岳麓区",430105:"开福区",430111:"雨花区",430121:"长沙县",430122:"望城区",430124:"宁乡县",430181:"浏阳市"},430200:{430202:"荷塘区",430203:"芦淞区",430204:"石峰区",430211:"天元区",430221:"株洲县",430223:"攸县",430224:"茶陵县",430225:"炎陵县",430281:"醴陵市"},430300:{430302:"雨湖区",430304:"岳塘区",430321:"湘潭县",430381:"湘乡市",430382:"韶山市"},430400:{430405:"珠晖区",430406:"雁峰区",430407:"石鼓区",430408:"蒸湘区",430412:"南岳区",430421:"衡阳县",430422:"衡南县",430423:"衡山县",430424:"衡东县",430426:"祁东县",430481:"耒阳市",430482:"常宁市"},430500:{430502:"双清区",430503:"大祥区",430511:"北塔区",430521:"邵东县",430522:"新邵县",430523:"邵阳县",430524:"隆回县",430525:"洞口县",430527:"绥宁县",430528:"新宁县",430529:"城步苗族自治县",430581:"武冈市"},430600:{430602:"岳阳楼区",430603:"云溪区",430611:"君山区",430621:"岳阳县",430623:"华容县",430624:"湘阴县",430626:"平江县",430681:"汨罗市",430682:"临湘市"},430700:{430702:"武陵区",430703:"鼎城区",430721:"安乡县",430722:"汉寿县",430723:"澧县",430724:"临澧县",430725:"桃源县",430726:"石门县",430781:"津市市"},430800:{430802:"永定区",430811:"武陵源区",430821:"慈利县",430822:"桑植县"},430900:{430902:"资阳区",430903:"赫山区",430921:"南县",430922:"桃江县",430923:"安化县",430981:"沅江市"},431e3:{431002:"北湖区",431003:"苏仙区",431021:"桂阳县",431022:"宜章县",431023:"永兴县",431024:"嘉禾县",431025:"临武县",431026:"汝城县",431027:"桂东县",431028:"安仁县",431081:"资兴市"},431100:{431102:"零陵区",431103:"冷水滩区",431121:"祁阳县",431122:"东安县",431123:"双牌县",431124:"道县",431125:"江永县",431126:"宁远县",431127:"蓝山县",431128:"新田县",431129:"江华瑶族自治县"},431200:{431202:"鹤城区",431221:"中方县",431222:"沅陵县",431223:"辰溪县",431224:"溆浦县",431225:"会同县",431226:"麻阳苗族自治县",431227:"新晃侗族自治县",431228:"芷江侗族自治县",431229:"靖州苗族侗族自治县",431230:"通道侗族自治县",431281:"洪江市"},431300:{431302:"娄星区",431321:"双峰县",431322:"新化县",431381:"冷水江市",431382:"涟源市"},433100:{433101:"吉首市",433122:"泸溪县",433123:"凤凰县",433124:"花垣县",433125:"保靖县",433126:"古丈县",433127:"永顺县",433130:"龙山县"},44e4:{440100:"广州市",440200:"韶关市",440300:"深圳市",440400:"珠海市",440500:"汕头市",440600:"佛山市",440700:"江门市",440800:"湛江市",440900:"茂名市",441200:"肇庆市",441300:"惠州市",441400:"梅州市",441500:"汕尾市",441600:"河源市",441700:"阳江市",441800:"清远市",441900:"东莞市",442e3:"中山市",442101:"东沙群岛",445100:"潮州市",445200:"揭阳市",445300:"云浮市"},440100:{440103:"荔湾区",440104:"越秀区",440105:"海珠区",440106:"天河区",440111:"白云区",440112:"黄埔区",440113:"番禺区",440114:"花都区",440115:"南沙区",440116:"萝岗区",440118:"增城区",440184:"从化区",440188:"东山区"},440200:{440203:"武江区",440204:"浈江区",440205:"曲江区",440222:"始兴县",440224:"仁化县",440229:"翁源县",440232:"乳源瑶族自治县",440233:"新丰县",440281:"乐昌市",440282:"南雄市"},440300:{440303:"罗湖区",440304:"福田区",440305:"南山区",440306:"宝安区",440307:"龙岗区",440308:"盐田区",440320:"光明新区",440321:"坪山新区",440322:"大鹏新区",440323:"龙华新区"},440400:{440402:"香洲区",440403:"斗门区",440404:"金湾区",440486:"金唐区",440487:"南湾区"},440500:{440507:"龙湖区",440511:"金平区",440512:"濠江区",440513:"潮阳区",440514:"潮南区",440515:"澄海区",440523:"南澳县"},440600:{440604:"禅城区",440605:"南海区",440606:"顺德区",440607:"三水区",440608:"高明区"},440700:{440703:"蓬江区",440704:"江海区",440705:"新会区",440781:"台山市",440783:"开平市",440784:"鹤山市",440785:"恩平市"},440800:{440802:"赤坎区",440803:"霞山区",440804:"坡头区",440811:"麻章区",440823:"遂溪县",440825:"徐闻县",440881:"廉江市",440882:"雷州市",440883:"吴川市"},440900:{440902:"茂南区",440903:"茂港区",440923:"电白区",440981:"高州市",440982:"化州市",440983:"信宜市"},441200:{441202:"端州区",441203:"鼎湖区",441223:"广宁县",441224:"怀集县",441225:"封开县",441226:"德庆县",441283:"高要区",441284:"四会市"},441300:{441302:"惠城区",441303:"惠阳区",441322:"博罗县",441323:"惠东县",441324:"龙门县",441325:"大亚湾区"},441400:{441402:"梅江区",441421:"梅县",441422:"大埔县",441423:"丰顺县",441424:"五华县",441426:"平远县",441427:"蕉岭县",441481:"兴宁市"},441500:{441502:"城区",441521:"海丰县",441523:"陆河县",441581:"陆丰市"},441600:{441602:"源城区",441621:"紫金县",441622:"龙川县",441623:"连平县",441624:"和平县",441625:"东源县"},441700:{441702:"江城区",441721:"阳西县",441723:"阳东区",441781:"阳春市"},441800:{441802:"清城区",441821:"佛冈县",441823:"阳山县",441825:"连山壮族瑶族自治县",441826:"连南瑶族自治县",441827:"清新区",441881:"英德市",441882:"连州市"},442e3:{442002:"中山火炬开发区",442003:"石岐区"},445100:{445102:"湘桥区",445121:"潮安区",445122:"饶平县",445185:"枫溪区"},445200:{445202:"榕城区",445221:"揭东区",445222:"揭西县",445224:"惠来县",445281:"普宁市",445284:"东山区"},445300:{445302:"云城区",445321:"新兴县",445322:"郁南县",445323:"云安区",445381:"罗定市"},45e4:{450100:"南宁市",450200:"柳州市",450300:"桂林市",450400:"梧州市",450500:"北海市",450600:"防城港市",450700:"钦州市",450800:"贵港市",450900:"玉林市",451e3:"百色市",451100:"贺州市",451200:"河池市",451300:"来宾市",451400:"崇左市"},450100:{450102:"兴宁区",450103:"青秀区",450105:"江南区",450107:"西乡塘区",450108:"良庆区",450109:"邕宁区",450110:"武鸣区",450123:"隆安县",450124:"马山县",450125:"上林县",450126:"宾阳县",450127:"横县"},450200:{450202:"城中区",450203:"鱼峰区",450204:"柳南区",450205:"柳北区",450221:"柳江县",450222:"柳城县",450223:"鹿寨县",450224:"融安县",450225:"融水苗族自治县",450226:"三江侗族自治县"},450300:{450302:"秀峰区",450303:"叠彩区",450304:"象山区",450305:"七星区",450311:"雁山区",450321:"阳朔县",450322:"临桂区",450323:"灵川县",450324:"全州县",450325:"兴安县",450326:"永福县",450327:"灌阳县",450328:"龙胜各族自治县",450329:"资源县",450330:"平乐县",450331:"荔浦县",450332:"恭城瑶族自治县"},450400:{450403:"万秀区",450404:"蝶山区",450405:"长洲区",450406:"龙圩区",450421:"苍梧县",450422:"藤县",450423:"蒙山县",450481:"岑溪市"},450500:{450502:"海城区",450503:"银海区",450512:"铁山港区",450521:"合浦县"},450600:{450602:"港口区",450603:"防城区",450621:"上思县",450681:"东兴市"},450700:{450702:"钦南区",450703:"钦北区",450721:"灵山县",450722:"浦北县"},450800:{450802:"港北区",450803:"港南区",450804:"覃塘区",450821:"平南县",450881:"桂平市"},450900:{450902:"玉州区",450903:"福绵区",450921:"容县",450922:"陆川县",450923:"博白县",450924:"兴业县",450981:"北流市"},451e3:{451002:"右江区",451021:"田阳县",451022:"田东县",451023:"平果县",451024:"德保县",451025:"靖西县",451026:"那坡县",451027:"凌云县",451028:"乐业县",451029:"田林县",451030:"西林县",451031:"隆林各族自治县"},451100:{451102:"八步区",451119:"平桂管理区",451121:"昭平县",451122:"钟山县",451123:"富川瑶族自治县"},451200:{451202:"金城江区",451221:"南丹县",451222:"天峨县",451223:"凤山县",451224:"东兰县",451225:"罗城仫佬族自治县",451226:"环江毛南族自治县",451227:"巴马瑶族自治县",451228:"都安瑶族自治县",451229:"大化瑶族自治县",451281:"宜州市"},451300:{451302:"兴宾区",451321:"忻城县",451322:"象州县",451323:"武宣县",451324:"金秀瑶族自治县",451381:"合山市"},451400:{451402:"江州区",451421:"扶绥县",451422:"宁明县",451423:"龙州县",451424:"大新县",451425:"天等县",451481:"凭祥市"},46e4:{460100:"海口市",460200:"三亚市",460300:"三沙市",469001:"五指山市",469002:"琼海市",469003:"儋州市",469005:"文昌市",469006:"万宁市",469007:"东方市",469025:"定安县",469026:"屯昌县",469027:"澄迈县",469028:"临高县",469031:"昌江黎族自治县",469033:"乐东黎族自治县",469034:"陵水黎族自治县",469035:"保亭黎族苗族自治县",469036:"琼中黎族苗族自治县"},460100:{460105:"秀英区",460106:"龙华区",460107:"琼山区",460108:"美兰区"},460200:{460201:"三亚市"},460300:{460321:"西沙群岛",460322:"南沙群岛",460323:"中沙群岛的岛礁及其海域"},5e5:{500100:"重庆市"},500100:{500101:"万州区",500102:"涪陵区",500103:"渝中区",500104:"大渡口区",500105:"江北区",500106:"沙坪坝区",500107:"九龙坡区",500108:"南岸区",500109:"北碚区",500110:"万盛区",500111:"双桥区",500112:"渝北区",500113:"巴南区",500114:"黔江区",500115:"长寿区",500199:"北部新区",500200:"两江新区",500222:"綦江区",500223:"潼南区",500224:"铜梁县",500225:"大足区",500226:"荣昌区",500227:"璧山区",500228:"梁平县",500229:"城口县",500230:"丰都县",500231:"垫江县",500232:"武隆县",500233:"忠县",500234:"开县",500235:"云阳县",500236:"奉节县",500237:"巫山县",500238:"巫溪县",500240:"石柱土家族自治县",500241:"秀山土家族苗族自治县",500242:"酉阳土家族苗族自治县",500243:"彭水苗族土家族自治县",500381:"江津区",500382:"合川区",500383:"永川区",500384:"南川区"},51e4:{510100:"成都市",510300:"自贡市",510400:"攀枝花市",510500:"泸州市",510600:"德阳市",510700:"绵阳市",510800:"广元市",510900:"遂宁市",511e3:"内江市",511100:"乐山市",511300:"南充市",511400:"眉山市",511500:"宜宾市",511600:"广安市",511700:"达州市",511800:"雅安市",511900:"巴中市",512e3:"资阳市",513200:"阿坝藏族羌族自治州",513300:"甘孜藏族自治州",513400:"凉山彝族自治州"},510100:{510104:"锦江区",510105:"青羊区",510106:"金牛区",510107:"武侯区",510108:"成华区",510112:"龙泉驿区",510113:"青白江区",510114:"新都区",510115:"温江区",510116:"天府新区",510117:"高新区",510121:"金堂县",510122:"双流县",510124:"郫县",510129:"大邑县",510131:"蒲江县",510132:"新津县",510181:"都江堰市",510182:"彭州市",510183:"邛崃市",510184:"崇州市"},510300:{510302:"自流井区",510303:"贡井区",510304:"大安区",510311:"沿滩区",510321:"荣县",510322:"富顺县"},510400:{510402:"东区",510403:"西区",510411:"仁和区",510421:"米易县",510422:"盐边县"},510500:{510502:"江阳区",510503:"纳溪区",510504:"龙马潭区",510521:"泸县",510522:"合江县",510524:"叙永县",510525:"古蔺县"},510600:{510603:"旌阳区",510623:"中江县",510626:"罗江县",510681:"广汉市",510682:"什邡市",510683:"绵竹市"},510700:{510703:"涪城区",510704:"游仙区",510722:"三台县",510723:"盐亭县",510724:"安县",510725:"梓潼县",510726:"北川羌族自治县",510727:"平武县",510751:"高新区",510781:"江油市"},510800:{510802:"利州区",510811:"昭化区",510812:"朝天区",510821:"旺苍县",510822:"青川县",510823:"剑阁县",510824:"苍溪县"},510900:{510903:"船山区",510904:"安居区",510921:"蓬溪县",510922:"射洪县",510923:"大英县"},511e3:{511002:"市中区",511011:"东兴区",511024:"威远县",511025:"资中县",511028:"隆昌县"},511100:{511102:"市中区",511111:"沙湾区",511112:"五通桥区",511113:"金口河区",511123:"犍为县",511124:"井研县",511126:"夹江县",511129:"沐川县",511132:"峨边彝族自治县",511133:"马边彝族自治县",511181:"峨眉山市"},511300:{511302:"顺庆区",511303:"高坪区",511304:"嘉陵区",511321:"南部县",511322:"营山县",511323:"蓬安县",511324:"仪陇县",511325:"西充县",511381:"阆中市"},511400:{511402:"东坡区",511421:"仁寿县",511422:"彭山区",511423:"洪雅县",511424:"丹棱县",511425:"青神县"},511500:{511502:"翠屏区",511521:"宜宾县",511522:"南溪区",511523:"江安县",511524:"长宁县",511525:"高县",511526:"珙县",511527:"筠连县",511528:"兴文县",511529:"屏山县"},511600:{511602:"广安区",511603:"前锋区",511621:"岳池县",511622:"武胜县",511623:"邻水县",511681:"华蓥市",511682:"市辖区"},511700:{511702:"通川区",511721:"达川区",511722:"宣汉县",511723:"开江县",511724:"大竹县",511725:"渠县",511781:"万源市"},511800:{511802:"雨城区",511821:"名山区",511822:"荥经县",511823:"汉源县",511824:"石棉县",511825:"天全县",511826:"芦山县",511827:"宝兴县"},511900:{511902:"巴州区",511903:"恩阳区",511921:"通江县",511922:"南江县",511923:"平昌县"},512e3:{512002:"雁江区",512021:"安岳县",512022:"乐至县",512081:"简阳市"},513200:{513221:"汶川县",513222:"理县",513223:"茂县",513224:"松潘县",513225:"九寨沟县",513226:"金川县",513227:"小金县",513228:"黑水县",513229:"马尔康县",513230:"壤塘县",513231:"阿坝县",513232:"若尔盖县",513233:"红原县"},513300:{513321:"康定区",513322:"泸定县",513323:"丹巴县",513324:"九龙县",513325:"雅江县",513326:"道孚县",513327:"炉霍县",513328:"甘孜县",513329:"新龙县",513330:"德格县",513331:"白玉县",513332:"石渠县",513333:"色达县",513334:"理塘县",513335:"巴塘县",513336:"乡城县",513337:"稻城县",513338:"得荣县"},513400:{513401:"西昌市",513422:"木里藏族自治县",513423:"盐源县",513424:"德昌县",513425:"会理县",513426:"会东县",513427:"宁南县",513428:"普格县",513429:"布拖县",513430:"金阳县",513431:"昭觉县",513432:"喜德县",513433:"冕宁县",513434:"越西县",513435:"甘洛县",513436:"美姑县",513437:"雷波县"},52e4:{520100:"贵阳市",520200:"六盘水市",520300:"遵义市",520400:"安顺市",522200:"铜仁市",522300:"黔西南布依族苗族自治州",522400:"毕节市",522600:"黔东南苗族侗族自治州",522700:"黔南布依族苗族自治州"},520100:{520102:"铜仁市",520103:"云岩区",520111:"花溪区",520112:"乌当区",520113:"白云区",520114:"小河区",520121:"开阳县",520122:"息烽县",520123:"修文县",520151:"观山湖区",520181:"清镇市"},520200:{520201:"钟山区",520203:"六枝特区",520221:"水城县",520222:"盘县"},520300:{520302:"红花岗区",520303:"汇川区",520321:"遵义县",520322:"桐梓县",520323:"绥阳县",520324:"正安县",520325:"道真仡佬族苗族自治县",520326:"务川仡佬族苗族自治县",520327:"凤冈县",520328:"湄潭县",520329:"余庆县",520330:"习水县",520381:"赤水市",520382:"仁怀市"},520400:{520402:"西秀区",520421:"平坝区",520422:"普定县",520423:"镇宁布依族苗族自治县",520424:"关岭布依族苗族自治县",520425:"紫云苗族布依族自治县"},522200:{522201:"碧江区",522222:"江口县",522223:"玉屏侗族自治县",522224:"石阡县",522225:"思南县",522226:"印江土家族苗族自治县",522227:"德江县",522228:"沿河土家族自治县",522229:"松桃苗族自治县",522230:"万山区"},522300:{522301:"兴义市",522322:"兴仁县",522323:"普安县",522324:"晴隆县",522325:"贞丰县",522326:"望谟县",522327:"册亨县",522328:"安龙县"},522400:{520502:"七星关区",520521:"大方县",520522:"黔西县",520523:"金沙县",520524:"织金县",520525:"纳雍县",520526:"威宁彝族回族苗族自治县",520527:"赫章县"},522600:{522601:"凯里市",522622:"黄平县",522623:"施秉县",522624:"三穗县",522625:"镇远县",522626:"岑巩县",522627:"天柱县",522628:"锦屏县",522629:"剑河县",522630:"台江县",522631:"黎平县",522632:"榕江县",522633:"从江县",522634:"雷山县",522635:"麻江县",522636:"丹寨县"},522700:{522701:"都匀市",522702:"福泉市",522722:"荔波县",522723:"贵定县",522725:"瓮安县",522726:"独山县",522727:"平塘县",522728:"罗甸县",522729:"长顺县",522730:"龙里县",522731:"惠水县",522732:"三都水族自治县"},53e4:{530100:"昆明市",530300:"曲靖市",530400:"玉溪市",530500:"保山市",530600:"昭通市",530700:"丽江市",530800:"普洱市",530900:"临沧市",532300:"楚雄彝族自治州",532500:"红河哈尼族彝族自治州",532600:"文山壮族苗族自治州",532800:"西双版纳傣族自治州",532900:"大理白族自治州",533100:"德宏傣族景颇族自治州",533300:"怒江傈僳族自治州",533400:"迪庆藏族自治州"},530100:{530102:"五华区",530103:"盘龙区",530111:"官渡区",530112:"西山区",530113:"东川区",530114:"呈贡区",530122:"晋宁县",530124:"富民县",530125:"宜良县",530126:"石林彝族自治县",530127:"嵩明县",530128:"禄劝彝族苗族自治县",530129:"寻甸回族彝族自治县",530181:"安宁市"},530300:{530302:"麒麟区",530321:"马龙县",530322:"陆良县",530323:"师宗县",530324:"罗平县",530325:"富源县",530326:"会泽县",530328:"沾益县",530381:"宣威市"},530400:{530402:"红塔区",530421:"江川县",530422:"澄江县",530423:"通海县",530424:"华宁县",530425:"易门县",530426:"峨山彝族自治县",530427:"新平彝族傣族自治县",530428:"元江哈尼族彝族傣族自治县"},530500:{530502:"隆阳区",530521:"施甸县",530522:"腾冲县",530523:"龙陵县",530524:"昌宁县"},530600:{530602:"昭阳区",530621:"鲁甸县",530622:"巧家县",530623:"盐津县",530624:"大关县",530625:"永善县",530626:"绥江县",530627:"镇雄县",530628:"彝良县",530629:"威信县",530630:"水富县"},530700:{530702:"古城区",530721:"玉龙纳西族自治县",530722:"永胜县",530723:"华坪县",530724:"宁蒗彝族自治县"},530800:{530802:"思茅区",530821:"宁洱哈尼族彝族自治县",530822:"墨江哈尼族自治县",530823:"景东彝族自治县",530824:"景谷傣族彝族自治县",530825:"镇沅彝族哈尼族拉祜族自治县",530826:"江城哈尼族彝族自治县",530827:"孟连傣族拉祜族佤族自治县",530828:"澜沧拉祜族自治县",530829:"西盟佤族自治县"},530900:{530902:"临翔区",530921:"凤庆县",530922:"云县",530923:"永德县",530924:"镇康县",530925:"双江拉祜族佤族布朗族傣族自治县",530926:"耿马傣族佤族自治县",530927:"沧源佤族自治县"},532300:{532301:"楚雄市",532322:"双柏县",532323:"牟定县",532324:"南华县",532325:"姚安县",532326:"大姚县",532327:"永仁县",532328:"元谋县",532329:"武定县",532331:"禄丰县"},532500:{532501:"个旧市",532502:"开远市",532522:"蒙自市",532523:"屏边苗族自治县",532524:"建水县",532525:"石屏县",532526:"弥勒市",532527:"泸西县",532528:"元阳县",532529:"红河县",532530:"金平苗族瑶族傣族自治县",532531:"绿春县",532532:"河口瑶族自治县"},532600:{532621:"文山市",532622:"砚山县",532623:"西畴县",532624:"麻栗坡县",532625:"马关县",532626:"丘北县",532627:"广南县",532628:"富宁县"},532800:{532801:"景洪市",532822:"勐海县",532823:"勐腊县"},532900:{532901:"大理市",532922:"漾濞彝族自治县",532923:"祥云县",532924:"宾川县",532925:"弥渡县",532926:"南涧彝族自治县",532927:"巍山彝族回族自治县",532928:"永平县",532929:"云龙县",532930:"洱源县",532931:"剑川县",532932:"鹤庆县"},533100:{533102:"瑞丽市",533103:"芒市",533122:"梁河县",533123:"盈江县",533124:"陇川县"},533300:{533321:"泸水县",533323:"福贡县",533324:"贡山独龙族怒族自治县",533325:"兰坪白族普米族自治县"},533400:{533421:"香格里拉市",533422:"德钦县",533423:"维西傈僳族自治县"},54e4:{540100:"拉萨市",542300:"日喀则市",542100:"昌都市",542200:"山南地区",542400:"那曲地区",542500:"阿里地区",542600:"林芝市"},540100:{540102:"城关区",540121:"林周县",540122:"当雄县",540123:"尼木县",540124:"曲水县",540125:"堆龙德庆县",540126:"达孜县",540127:"墨竹工卡县"},542300:{542301:"桑珠孜区",542322:"南木林县",542323:"江孜县",542324:"定日县",542325:"萨迦县",542326:"拉孜县",542327:"昂仁县",542328:"谢通门县",542329:"白朗县",542330:"仁布县",542331:"康马县",542332:"定结县",542333:"仲巴县",542334:"亚东县",542335:"吉隆县",542336:"聂拉木县",542337:"萨嘎县",542338:"岗巴县"},542100:{542121:"卡若区",542122:"江达县",542123:"贡觉县",542124:"类乌齐县",542125:"丁青县",542126:"察雅县",542127:"八宿县",542128:"左贡县",542129:"芒康县",542132:"洛隆县",542133:"边坝县"},542200:{542221:"乃东县",542222:"扎囊县",542223:"贡嘎县",542224:"桑日县",542225:"琼结县",542226:"曲松县",542227:"措美县",542228:"洛扎县",542229:"加查县",542231:"隆子县",542232:"错那县",542233:"浪卡子县"},542400:{542421:"那曲县",542422:"嘉黎县",542423:"比如县",542424:"聂荣县",542425:"安多县",542426:"申扎县",542427:"索县",542428:"班戈县",542429:"巴青县",542430:"尼玛县",542432:"双湖县"},542500:{542521:"普兰县",542522:"札达县",542523:"噶尔县",542524:"日土县",542525:"革吉县",542526:"改则县",542527:"措勤县"},542600:{542621:"巴宜区",542622:"工布江达县",542623:"米林县",542624:"墨脱县",542625:"波密县",542626:"察隅县",542627:"朗县"},61e4:{610100:"西安市",610200:"铜川市",610300:"宝鸡市",610400:"咸阳市",610500:"渭南市",610600:"延安市",610700:"汉中市",610800:"榆林市",610900:"安康市",611e3:"商洛市"},610100:{610102:"新城区",610103:"碑林区",610104:"莲湖区",610111:"灞桥区",610112:"未央区",610113:"雁塔区",610114:"阎良区",610115:"临潼区",610116:"长安区",610117:"高陵区",610122:"蓝田县",610124:"周至县",610125:"户县",610126:"高陵区",610199:"曲江新区"},610200:{610202:"王益区",610203:"印台区",610204:"耀州区",610222:"宜君县"},610300:{610302:"渭滨区",610303:"金台区",610304:"陈仓区",610322:"凤翔县",610323:"岐山县",610324:"扶风县",610326:"眉县",610327:"陇县",610328:"千阳县",610329:"麟游县",610330:"凤县",610331:"太白县"},610400:{610402:"秦都区",610403:"杨陵区",610404:"渭城区",610422:"三原县",610423:"泾阳县",610424:"乾县",610425:"礼泉县",610426:"永寿县",610427:"彬县",610428:"长武县",610429:"旬邑县",610430:"淳化县",610431:"武功县",610481:"兴平市"},610500:{610502:"临渭区",610521:"华县",610522:"潼关县",610523:"大荔县",610524:"合阳县",610525:"澄城县",610526:"蒲城县",610527:"白水县",610528:"富平县",610581:"韩城市",610582:"华阴市"},610600:{610602:"宝塔区",610621:"延长县",610622:"延川县",610623:"子长县",610624:"安塞县",610625:"志丹县",610626:"吴起县",610627:"甘泉县",610628:"富县",610629:"洛川县",610630:"宜川县",610631:"黄龙县",610632:"黄陵县"},610700:{610702:"汉台区",610721:"南郑县",610722:"城固县",610723:"洋县",610724:"西乡县",610725:"勉县",610726:"宁强县",610727:"略阳县",610728:"镇巴县",610729:"留坝县",610730:"佛坪县"},610800:{610802:"榆阳区",610821:"神木县",610822:"府谷县",610823:"横山县",610824:"靖边县",610825:"定边县",610826:"绥德县",610827:"米脂县",610828:"佳县",610829:"吴堡县",610830:"清涧县",610831:"子洲县"},610900:{610902:"汉滨区",610921:"汉阴县",610922:"石泉县",610923:"宁陕县",610924:"紫阳县",610925:"岚皋县",610926:"平利县",610927:"镇坪县",610928:"旬阳县",610929:"白河县"},611e3:{611002:"商州区",611021:"洛南县",611022:"丹凤县",611023:"商南县",611024:"山阳县",611025:"镇安县",611026:"柞水县"},62e4:{620100:"兰州市",620200:"嘉峪关市",620300:"金昌市",620400:"白银市",620500:"天水市",620600:"武威市",620700:"张掖市",620800:"平凉市",620900:"酒泉市",621e3:"庆阳市",621100:"定西市",621200:"陇南市",622900:"临夏回族自治州",623e3:"甘南藏族自治州"},620100:{1000665:"兰州新区",620102:"城关区",620103:"七里河区",620104:"西固区",620105:"安宁区",620111:"红古区",620121:"永登县",620122:"皋兰县",620123:"榆中县"},620300:{620302:"金川区",620321:"永昌县"},620400:{620402:"白银区",620403:"平川区",620421:"靖远县",620422:"会宁县",620423:"景泰县"},620500:{620502:"秦州区",620503:"麦积区",620521:"清水县",620522:"秦安县",620523:"甘谷县",620524:"武山县",620525:"张家川回族自治县"},620600:{620602:"凉州区",620621:"民勤县",620622:"古浪县",620623:"天祝藏族自治县"},620700:{620702:"甘州区",620721:"肃南裕固族自治县",620722:"民乐县",620723:"临泽县",620724:"高台县",620725:"山丹县"},620800:{620802:"崆峒区",620821:"泾川县",620822:"灵台县",620823:"崇信县",620824:"华亭县",620825:"庄浪县",620826:"静宁县"},620900:{620902:"肃州区",620921:"金塔县",620922:"瓜州县",620923:"肃北蒙古族自治县",620924:"阿克塞哈萨克族自治县",620981:"玉门市",620982:"敦煌市"},621e3:{621002:"西峰区",621021:"庆城县",621022:"环县",621023:"华池县",621024:"合水县",621025:"正宁县",621026:"宁县",621027:"镇原县"},621100:{621102:"安定区",621121:"通渭县",621122:"陇西县",621123:"渭源县",621124:"临洮县",621125:"漳县",621126:"岷县"},621200:{621202:"武都区",621221:"成县",621222:"文县",621223:"宕昌县",621224:"康县",621225:"西和县",621226:"礼县",621227:"徽县",621228:"两当县"},622900:{622901:"临夏市",622921:"临夏县",622922:"康乐县",622923:"永靖县",622924:"广河县",622925:"和政县",622926:"东乡族自治县",622927:"积石山保安族东乡族撒拉族自治县"},623e3:{623001:"合作市",623021:"临潭县",623022:"卓尼县",623023:"舟曲县",623024:"迭部县",623025:"玛曲县",623026:"碌曲县",623027:"夏河县"},63e4:{630100:"西宁市",632100:"海东市",632200:"海北藏族自治州",632300:"黄南藏族自治州",632500:"海南藏族自治州",632600:"果洛藏族自治州",632700:"玉树藏族自治州",632800:"海西蒙古族藏族自治州"},630100:{630102:"城东区",630103:"城中区",630104:"城西区",630105:"城北区",630121:"大通回族土族自治县",630122:"湟中县",630123:"湟源县"},632100:{632121:"平安区",632122:"民和回族土族自治县",632123:"乐都区",632126:"互助土族自治县",632127:"化隆回族自治县",632128:"循化撒拉族自治县"},632200:{632221:"门源回族自治县",632222:"祁连县",632223:"海晏县",632224:"刚察县"},632300:{632321:"同仁县",632322:"尖扎县",632323:"泽库县",632324:"河南蒙古族自治县"},632500:{632521:"共和县",632522:"同德县",632523:"贵德县",632524:"兴海县",632525:"贵南县"},632600:{632621:"玛沁县",632622:"班玛县",632623:"甘德县",632624:"达日县",632625:"久治县",632626:"玛多县"},632700:{632721:"玉树市",632722:"杂多县",632723:"称多县",632724:"治多县",632725:"囊谦县",632726:"曲麻莱县"},632800:{632801:"格尔木市",632802:"德令哈市",632821:"乌兰县",632822:"都兰县",632823:"天峻县"},64e4:{640100:"银川市",640200:"石嘴山市",640300:"吴忠市",640400:"固原市",640500:"中卫市"},640100:{640104:"兴庆区",640105:"西夏区",640106:"金凤区",640121:"永宁县",640122:"贺兰县",640181:"灵武市"},640200:{640202:"大武口区",640205:"惠农区",640221:"平罗县"},640300:{640302:"利通区",640303:"红寺堡区",640323:"盐池县",640324:"同心县",640381:"青铜峡市"},640400:{640402:"原州区",640422:"西吉县",640423:"隆德县",640424:"泾源县",640425:"彭阳县"},640500:{640502:"沙坡头区",640521:"中宁县",640522:"海原县"},65e4:{650100:"乌鲁木齐市",650200:"克拉玛依市",652100:"吐鲁番市",652200:"哈密地区",652300:"昌吉回族自治州",652700:"博尔塔拉蒙古自治州",652800:"巴音郭楞蒙古自治州",652900:"阿克苏地区",653e3:"克孜勒苏柯尔克孜自治州",653100:"喀什地区",653200:"和田地区",654e3:"伊犁哈萨克自治州",654200:"塔城地区",654300:"阿勒泰地区",659001:"石河子市",659002:"阿拉尔市",659003:"图木舒克市",659004:"五家渠市",659005:"北屯市",659006:"铁门关市",659008:"可克达拉市"},650100:{650102:"天山区",650103:"沙依巴克区",650104:"新市区",650105:"水磨沟区",650106:"头屯河区",650107:"达坂城区",650109:"米东区",650121:"乌鲁木齐县"},650200:{650202:"独山子区",650203:"克拉玛依区",650204:"白碱滩区",650205:"乌尔禾区"},652100:{652101:"高昌区",652122:"鄯善县",652123:"托克逊县"},652200:{652201:"哈密市",652222:"巴里坤哈萨克自治县",652223:"伊吾县"},652300:{652301:"昌吉市",652302:"阜康市",652323:"呼图壁县",652324:"玛纳斯县",652325:"奇台县",652327:"吉木萨尔县",652328:"木垒哈萨克自治县"},652700:{652701:"博乐市",652702:"阿拉山口市",652722:"精河县",652723:"温泉县"},652800:{652801:"库尔勒市",652822:"轮台县",652823:"尉犁县",652824:"若羌县",652825:"且末县",652826:"焉耆回族自治县",652827:"和静县",652828:"和硕县",652829:"博湖县"},652900:{652901:"阿克苏市",652922:"温宿县",652923:"库车县",652924:"沙雅县",652925:"新和县",652926:"拜城县",652927:"乌什县",652928:"阿瓦提县",652929:"柯坪县"},653e3:{653001:"阿图什市",653022:"阿克陶县",653023:"阿合奇县",653024:"乌恰县"},653100:{653101:"喀什市",653121:"疏附县",653122:"疏勒县",653123:"英吉沙县",653124:"泽普县",653125:"莎车县",653126:"叶城县",653127:"麦盖提县",653128:"岳普湖县",653129:"伽师县",653130:"巴楚县",653131:"塔什库尔干塔吉克自治县"},653200:{653201:"和田市",653221:"和田县",653222:"墨玉县",653223:"皮山县",653224:"洛浦县",653225:"策勒县",653226:"于田县",653227:"民丰县"},654e3:{654002:"伊宁市",654003:"奎屯市",654004:"霍尔果斯市",654021:"伊宁县",654022:"察布查尔锡伯自治县",654023:"霍城县",654024:"巩留县",654025:"新源县",654026:"昭苏县",654027:"特克斯县",654028:"尼勒克县"},654200:{654201:"塔城市",654202:"乌苏市",654221:"额敏县",654223:"沙湾县",654224:"托里县",654225:"裕民县",654226:"和布克赛尔蒙古自治县"},654300:{654301:"阿勒泰市",654321:"布尔津县",654322:"富蕴县",654323:"福海县",654324:"哈巴河县",654325:"青河县",654326:"吉木乃县"},81e4:{810100:"九龙",810200:"香港岛",810300:"新界"},810100:{810101:"观塘区",810102:"黄大仙区",810103:"九龙城区",810104:"深水埗区",810105:"油尖旺区"},810200:{810201:"东区",810202:"南区",810203:"湾仔区",810204:"中西区"},810300:{810301:"北区",810302:"大埔区",810303:"葵青区",810304:"离岛区",810305:"荃湾区",810306:"沙田区",810307:"屯门区",810308:"西贡区",810309:"元朗区"},82e4:{820100:"离岛",820200:"澳门半岛"},820100:{820101:"嘉模堂区",820102:"路氹填海区",820103:"圣方济各堂区"},820200:{820201:"大堂区",820202:"风顺堂区",820203:"花地玛堂区",820204:"花王堂区",820205:"望德堂区"},83e4:{830100:"台北市",830200:"新北市",830300:"桃园市",830400:"台中市",830500:"台南市",830600:"高雄市",830700:"基隆市",830800:"新竹市",830900:"嘉义市"},830100:{830101:"中正区",830102:"大同区",830103:"中山区",830104:"万华区",830105:"信义区",830106:"松山区",830107:"大安区",830108:"南港区",830109:"北投区",830110:"内湖区",830111:"士林区",830112:"文山区"},830200:{830201:"板桥区",830202:"土城区",830203:"新庄区",830204:"新店区",830205:"深坑区",830206:"石碇区",830207:"坪林区",830208:"乌来区",830209:"五股区",830210:"八里区",830211:"林口区",830212:"淡水区",830213:"中和区",830214:"永和区",830215:"三重区",830216:"芦洲区",830217:"泰山区",830218:"树林区",830219:"莺歌区",830220:"三峡区",830221:"汐止区",830222:"金山区",830223:"万里区",830224:"三芝区",830225:"石门区",830226:"瑞芳区",830227:"贡寮区",830228:"双溪区",830229:"平溪区"},830300:{830301:"桃园区",830302:"中坜区",830303:"平镇区",830304:"八德区",830305:"杨梅区",830306:"芦竹区",830307:"大溪区",830308:"龙潭区",830309:"龟山区",830310:"大园区",830311:"观音区",830312:"新屋区",830313:"复兴区"},830400:{830401:"中区",830402:"东区",830403:"西区",830404:"南区",830405:"北区",830406:"西屯区",830407:"南屯区",830408:"北屯区",830409:"丰原区",830410:"大里区",830411:"太平区",830412:"东势区",830413:"大甲区",830414:"清水区",830415:"沙鹿区",830416:"梧栖区",830417:"后里区",830418:"神冈区",830419:"潭子区",830420:"大雅区",830421:"新小区",830422:"石冈区",830423:"外埔区",830424:"大安区",830425:"乌日区",830426:"大肚区",830427:"龙井区",830428:"雾峰区",830429:"和平区"},830500:{830501:"中西区",830502:"东区",830503:"南区",830504:"北区",830505:"安平区",830506:"安南区",830507:"永康区",830508:"归仁区",830509:"新化区",830510:"左镇区",830511:"玉井区",830512:"楠西区",830513:"南化区",830514:"仁德区",830515:"关庙区",830516:"龙崎区",830517:"官田区",830518:"麻豆区",830519:"佳里区",830520:"西港区",830521:"七股区",830522:"将军区",830523:"学甲区",830524:"北门区",830525:"新营区",830526:"后壁区",830527:"白河区",830528:"东山区",830529:"六甲区",830530:"下营区",830531:"柳营区",830532:"盐水区",830533:"善化区",830534:"大内区",830535:"山上区",830536:"新市区",830537:"安定区"},830600:{830601:"楠梓区",830602:"左营区",830603:"鼓山区",830604:"三民区",830605:"盐埕区",830606:"前金区",830607:"新兴区",830608:"苓雅区",830609:"前镇区",830610:"旗津区",830611:"小港区",830612:"凤山区",830613:"大寮区",830614:"鸟松区",830615:"林园区",830616:"仁武区",830617:"大树区",830618:"大社区",830619:"冈山区",830620:"路竹区",830621:"桥头区",830622:"梓官区",830623:"弥陀区",830624:"永安区",830625:"燕巢区",830626:"阿莲区",830627:"茄萣区",830628:"湖内区",830629:"旗山区",830630:"美浓区",830631:"内门区",830632:"杉林区",830633:"甲仙区",830634:"六龟区",830635:"茂林区",830636:"桃源区",830637:"那玛夏区"},830700:{830701:"中正区",830702:"七堵区",830703:"暖暖区",830704:"仁爱区",830705:"中山区",830706:"安乐区",830707:"信义区"},830800:{830801:"东区",830802:"北区",830803:"香山区"},830900:{830901:"东区",830902:"西区"}};"undefined"!=typeof window&&(window.ChineseDistricts=ChineseDistricts),define("plugins/lay-module/cityPicker/city-picker-data",function(){}),define("plugins/require-css/css.min!plugins/lay-module/cityPicker/city-picker",[],function(){}),layui.define(["jquery"],function(e){var t=layui.$;if(void 0===ChineseDistricts)throw new Error('The file "city-picker.data.js" must be included first!');var n="change.citypicker",i=function(e,n){this.PROVINCE="provinceId",this.CITY="cityId",this.DISTRICT="districtId",this.$element=t(e),this.$dropdown=null,this.options=t.extend({},i.DEFAULTS,t.isPlainObject(n)&&n),this.active=!1,this.dems=[],this.needBlur=!1,this.init()};i.prototype={constructor:i,init:function(){this.defineDems(),this.render(),this.bind(),this.active=!0;var e=this;t(".icon_ca").on("click",function(){t("#"+this.PROVINCE).val(),t("#"+this.CITY).val(),t("#"+this.DISTRICT).val(),e.reset()})},render:function(){ -var e=this.getPosition(),n=this.$element.attr("placeholder")||this.options.placeholder,i=''+(n?''+n+"":"")+'
        ',o='
        省份'+(this.includeDem(this.CITY)?'城市':"")+(this.includeDem(this.DISTRICT)?'区县':"")+'
        '+(this.includeDem(this.CITY)?'
        ':"")+(this.includeDem(this.DISTRICT)?'
        ':"")+"
        ";this.$element.addClass("city-picker-input"),this.$textspan=t(i).insertBefore(this.$element),this.$dropdown=t(o).insertAfter(this.$textspan);var r=this.$dropdown.find(".city-select");t.each(this.dems,t.proxy(function(e,t){this["$"+t]=r.filter("."+t)},this)),this.refresh()},refresh:function(e){this.$dropdown.find(".city-select").data("item",null);var n=this.$element.val()||"";n=n.split("/"),t.each(this.dems,t.proxy(function(t,i){n[t]&&t.99&&(n=1),n=100*n+"%"),{top:e.top||0,left:e.left||0,height:t,width:n}},getSize:function(e){var n,i,o;return e.is(":visible")?o={width:e.outerWidth(),height:e.outerHeight()}:(n=t("
        ").appendTo(t("body")),n.css({position:"absolute !important",visibility:"hidden !important",display:"block !important"}),i=e.clone().appendTo(n),o={width:i.outerWidth(),height:i.outerHeight()},n.remove()),o},getWidthStyle:function(e,n){return e="100%",this.options.responsive&&!t.isNumeric(e)?"width:"+e+";":"width:"+(n?Math.max(320,e):e)+"px;"},bind:function(){var e=this;t(document).on("click",this._mouteclick=function(n){var i,o,r,a=t(n.target);a.is(".city-picker-span")?o=a:a.is(".city-picker-span *")&&(o=a.parents(".city-picker-span")),a.is(".city-picker-input")&&(r=a),a.is(".city-picker-dropdown")?i=a:a.is(".city-picker-dropdown *")&&(i=a.parents(".city-picker-dropdown")),(!r&&!o&&!i||o&&o.get(0)!==e.$textspan.get(0)||r&&r.get(0)!==e.$element.get(0)||i&&i.get(0)!==e.$dropdown.get(0))&&e.close(!0)}),this.$element.on("change",this._changeElement=t.proxy(function(){this.close(!0),this.refresh(!0)},this)).on("focus",this._focusElement=t.proxy(function(){this.needBlur=!0,this.open()},this)).on("blur",this._blurElement=t.proxy(function(){this.needBlur&&(this.needBlur=!1,this.close(!0))},this)),this.$textspan.on("click",function(n){var i,o=t(n.target);e.needBlur=!1,o.is(".select-item")?(i=o.data("count"),e.open(i)):e.$dropdown.is(":visible")?e.close():e.open()}).on("mousedown",function(){e.needBlur=!1}),this.$dropdown.on("click",".city-select a",function(){var i=t(this).parents(".city-select"),o=i.find("a.active"),r=0===i.next().length;o.removeClass("active"),t(this).addClass("active"),o.data("code")!==t(this).data("code")&&(i.data("item",{address:t(this).attr("title"),code:t(this).data("code")}),t(this).trigger(n),e.feedText(),e.feedVal(),r&&e.close())}).on("click",".city-select-tab a",function(){if(!t(this).hasClass("active")){var n=t(this).data("count");e.tab(n)}}).on("mousedown",function(){e.needBlur=!1}),this["$"+this.PROVINCE]&&this["$"+this.PROVINCE].on(n,this._changeProvince=t.proxy(function(){this.output(this.CITY),this.output(this.DISTRICT),this.tab(this.CITY)},this)),this["$"+this.CITY]&&this["$"+this.CITY].on(n,this._changeCity=t.proxy(function(){this.output(this.DISTRICT),this.tab(this.DISTRICT)},this))},open:function(e){e=e||this.PROVINCE,this.$dropdown.show(),this.$textspan.addClass("open").addClass("focus"),this.tab(e)},close:function(e){this.$dropdown.hide(),this.$textspan.removeClass("open"),e&&this.$textspan.removeClass("focus")},unbind:function(){t(document).off("click",this._mouteclick),this.$element.off("change",this._changeElement),this.$element.off("focus",this._focusElement),this.$element.off("blur",this._blurElement),this.$textspan.off("click"),this.$textspan.off("mousedown"),this.$dropdown.off("click"),this.$dropdown.off("mousedown"),this["$"+this.PROVINCE]&&this["$"+this.PROVINCE].off(n,this._changeProvince),this.$city&&this.$city.off(n,this._changeCity)},getText:function(){var e="";return this.$dropdown.find(".city-select").each(function(){var n=t(this).data("item"),i=t(this).data("count");n&&(e+=(t(this).hasClass("province")?"":"/")+''+n.address+'')}),e},getPlaceHolder:function(){return this.$element.attr("placeholder")||this.options.placeholder},feedText:function(){this.getText()?(this.$textspan.find(">.placeholder").hide(),this.$textspan.find(">.title").children("input").val(""),this.$textspan.find(">.title").html(this.getText()).show()):(this.$textspan.find(">.placeholder").text(this.getPlaceHolder()).show(),this.$textspan.find(">.title").html("").hide())},getVal:function(){var e="";return this.$dropdown.find(".city-select").each(function(){var n=t(this).data("item");n&&(e+=(t(this).hasClass("province")?"":"/")+n.address)}),e},feedVal:function(){this.$element.val(this.getVal())},output:function(e){var n,i,o,r,a=this.options,s=this.PROVINCE,l=this.CITY,c=this.DISTRICT,d=this["$"+e],u=e===s?{}:[],h=null;d&&d.length&&(n=d.data("item"),r=(n?n.address:null)||a[e],o=e===s?86:e===l?this["$"+s]&&this["$"+s].find(".active").data("code"):e===c?this["$"+l]&&this["$"+l].find(".active").data("code"):o,i=t.isNumeric(o)?ChineseDistricts[o]:null,t.isPlainObject(i)&&t.each(i,function(t,n){var i;if(e===s){i=[];for(var o=0;o'),i.push("
        "+e+"
        "),t.each(a,function(e,t){i.push(''+(r?o.simplize(t.address,n):t.address)+"")}),i.push("
        ")}),i.join("")},getList:function(e,n){var i=[],o=this,r=this.options.simple;return i.push('
        '),t.each(e,function(e,t){i.push(''+(r?o.simplize(t.address,n):t.address)+"")}),i.push("
        "),i.join("")},simplize:function(e,t){return e=e||"",t===this.PROVINCE?e.replace(/[省,市,自治区,壮族,回族,维吾尔]/g,""):t===this.CITY?e.replace(/[市,地区,回族,蒙古,苗族,白族,傣族,景颇族,藏族,彝族,壮族,傈僳族,布依族,侗族]/g,"").replace("哈萨克","").replace("自治州","").replace(/自治县/,""):t===this.DISTRICT?e.length>2?e.replace(/[市,区,县,旗]/g,""):e:void 0},tab:function(e){var t=this.$dropdown.find(".city-select"),n=this.$dropdown.find(".city-select-tab > a"),i=this["$"+e],o=this.$dropdown.find('.city-select-tab > a[data-count="'+e+'"]');i&&(t.hide(),i.show(),n.removeClass("active"),o.addClass("active"))},reset:function(){this.$element.val(null).trigger("change")},setValue:function(e){this.$element.val(e).trigger("change")},destroy:function(){this.unbind(),this.$element.removeData("citypicker").removeClass("city-picker-input"),this.$textspan.remove(),this.$dropdown.remove()}},i.DEFAULTS={simple:!1,responsive:!1,placeholder:"请选择省/市/区",level:"district",provincename:"",cityname:"",districtname:"",province:"河南省",city:"",district:""},i.setDefaults=function(e){t.extend(i.DEFAULTS,e)},i.other=t.fn.citypicker,t.fn.citypicker=function(e){var n=[].slice.call(arguments,1);return this.each(function(){var o,r,a=t(this),s=a.data("citypicker");if(!s){if(/destroy/.test(e))return;o=t.extend({},a.data(),t.isPlainObject(e)&&e),a.data("citypicker",s=new i(this,o))}"string"==typeof e&&t.isFunction(r=s[e])&&r.apply(s,n)})},t.fn.citypicker.Constructor=i,t.fn.citypicker.setDefaults=i.setDefaults,t.fn.citypicker.noConflict=function(){return t.fn.citypicker=i.other,this},e("cityPicker",i)}),define("cityPicker",["plugins/lay-module/cityPicker/city-picker-data","css!plugins/lay-module/cityPicker/city-picker.css"],function(){}),layui.define([],function(e){function t(e){return e instanceof Date?e:isNaN(e)?/^\d+$/.test(e)?new Date(n(e)):(e=(e||"").trim().replace(/\.\d+/,"").replace(/-/,"/").replace(/-/,"/").replace(/(\d)T(\d)/,"$1 $2").replace(/Z/," UTC").replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"),new Date(e)):new Date(n(e))}function n(e){return parseInt(e)}function i(e,t,i){t=f[t]?t:f[i]?i:"zh_CN";for(var o=0,r=e<0?1:0,a=e=Math.abs(e);e>=g[o]&&o(0===o?9:1)&&(o+=1),f[t](e,o,a)[r].replace("%s",e)}function o(e,n){return((n=n?t(n):new Date)-t(e))/1e3}function r(e){for(var t=1,n=0,i=Math.abs(e);e>=g[n]&&n1&&(n+="s"),[e+" "+n+" ago","in "+e+" "+n]},zh_CN:function(e,t){if(0===t)return["刚刚","片刻后"];var n=p[parseInt(t/2)];return[e+n+"前",e+n+"后"]}},g=[60,60,24,7,365/7/12,12],m=6,y="datetime",v="data-tid",b={};d.prototype.doRender=function(e,t,n){var a,s=o(t,this.nowDate),c=this;e.innerHTML=i(s,n,this.defaultLocale),b[a=setTimeout(function(){c.doRender(e,t,n),delete b[a]},Math.min(1e3*r(s),2147483647))]=0,l(e,a)},d.prototype.format=function(e,t){return i(o(e,this.nowDate),t,this.defaultLocale)},d.prototype.render=function(e,t){void 0===e.length&&(e=[e]);for(var n=0,i=e.length;n0&&(i=r.width());var a=n.config.elem.parents(".layui-input-group");a.length>0&&(i-=a.width()),n.config.elem.wrap('
        '),n.config.elem.after('
        '),n.render()};a.prototype.render=function(){var e=this;if(e.config.url)return void t.get(e.config.url,{},function(t){var n=[];n=e.config.parseOptions?e.config.parseOptions(t):t,e.doRender(n)});e.doRender(e.config.options)},a.prototype.doRender=function(e){var n=this,i=e.map(function(e){var t={};return t[n.config.customName.id]=e[n.config.customName.id],t[n.config.customName.title]=e[n.config.customName.title],t[n.config.customName.selected]=e[n.config.customName.selected],t}),o=i.filter(function(e){return e[n.config.customName.selected]}).map(function(e){return e[n.config.customName.id]});n.config.elem.val(o.join(n.config.valueSeparator)),n.config.elem.parent().on("click",".multiple-select-selection-item-remove",function(e){e.stopPropagation();var i=t(this).parent().data("id"),o=n.getOptionById(i);n.remove(o)}),n.context={keyword:"",filteredOptions:null,selectedIds:o,options:i,dropdownMenuScrollTop:0},n.renderDropdown(),n.renderSelection()},a.prototype.clear=function(){this.context.selectedIds=[],this.reloadDropdownData(this.buildRenderOptions())},a.prototype.remove=function(e){var t=this;this.context.selectedIds=this.context.selectedIds.filter(function(n){return n!=e[t.config.customName.id]}),this.reloadDropdownData(this.buildRenderOptions())},a.prototype.select=function(e){e=layui.isArray(e)?e:[e];for(var t=e.length,n=0;n
        ','
          ',e.length>0?e.map(function(e){return'
        • '+e[t.config.customName.title]+"
        • "}).join(""):'
          '+this.config.unfilteredText+"
          ","
        "].join("")},a.prototype.reloadDropdownData=function(e){e&&e.length===this.getAllOptions().length&&this.resetSearch();var t=this.buildRenderOptions(e);this.dropdown.reloadData({content:this.buildDropdownContent(t)}),this.renderSelection()},a.prototype.getSearchInput=function(){if(this.panel)return this.panel.find("div.multiple-select-search > input")},a.prototype.renderDropdown=function(){var e=this;e.dropdown=i.render({elem:e.config.elem.parent(),style:"width:"+e.config.elem.outerWidth()+"px",content:e.buildDropdownContent(e.getAllOptions()),ready:function(n,i){function o(t){var n=e.filterOptions(t);if(0===n.length&&e.config.allowCreate){var i={};i[e.config.customName.id]=t,i[e.config.customName.title]=t,n.push(i)}e.context.keyword=t,e.context.filteredOptions=n,e.reloadDropdownData(e.context.filteredOptions)}e.panel=n,i.addClass("multiple-select-panel-opended"),e.context.dropdownMenuScrollTop>0&&n.find(".layui-dropdown-menu").scrollTop(e.context.dropdownMenuScrollTop);var r=!1,a=e.getSearchInput();a.on("input",function(){r||o(t(this).val())}).on("compositionstart",function(){r=!0}).on("compositionend",function(){r=!1,o(t(this).val())}),""!==e.context.keyword&&a.val(e.context.keyword).focus()},click:function(n,i){if(!t(this).hasClass("layui-menu-item-none")){var o=i.data("value"),r=e.getOptionById(o);if(!r){if(!e.config.allowCreate)return;var r={};r[e.config.customName.id]=o,r[e.config.customName.title]=o,r[e.config.customName.selected]=!0,e.context.options.push(r)}return e.context.dropdownMenuScrollTop=i.parent().scrollTop(),-1===e.context.selectedIds.indexOf(r[e.config.customName.id])?e.select(r):e.remove(r),!1}},close:function(t){e.context.keyword="",e.context.filteredOptions=null,e.reloadDropdownData(),t.removeClass("multiple-select-panel-opended")}})},a.prototype.getOptionById=function(e){var t=this;return this.getAllOptions().filter(function(n){return n[t.config.customName.id]==e})[0]},a.prototype.renderSelection=function(){var e=this,t=e.config.elem.parent(),i=e.getSelectedOptions(),o=i.length>1&&e.config.collapseSelected?[i[0]]:i,r="";i.length>0&&(r=n('
        {{# layui.each(d.options, function(index, option) { }}
        {{= option[d.title] }}
        {{# }) }} {{# if (d.collapseSelected && d.total > 1) { }}
        + {{ d.total }}
        {{# } }}
        ').render({total:i.length,options:o,id:e.config.customName.id,title:e.config.customName.title,collapseSelected:e.config.collapseSelected})),t.find(".multiple-select-selection").remove(),""!==r&&t.append(r),this.config.elem.val(this.context.selectedIds.join(this.context.valueSeparator))},a.prototype.getSelectedOptions=function(){for(var e=this.context.selectedIds,t=[],n=0;n=n.selectArray.length)){var o,r,a,s,l,c=n.selectArray[t],d=c.data("url"),u=void 0===c.data("jsonSpace")?n.settings.jsonSpace:c.data("jsonSpace"),h={};if(i.clear.call(n,t),"string"==typeof d&&d.length){if(t>0)for(var p=0,f=1;p'+String(s)+"";if("string"==typeof c&&c.length){"string"==typeof d&&d.length||(d=c);for(var h=0,p=i.length;h'+String(i[h][c])+""}else for(var h=0,p=i.length;h'+String(i[h])+"";r.html(u).prop("disabled",!1).css({display:"",visibility:""}),"string"==typeof r.attr("data-value")&&(r.val(String(r.attr("data-value"))).removeAttr("data-value"),r[0].selectedIndex<0&&(r[0].options[0].selected=!0)),(a||r[0].selectedIndex>0)&&r.trigger("change"),n.render("select")}},i.selectChange=function(e){var t=this;if("string"==typeof e&&e.length){var n;e=e.replace(/\s+/g,","),e=","+e+",";for(var o=0,r=t.selectArray.length;o-1){n=o;break}"number"==typeof n&&n>-1&&(n+=1,i.getOptionData.call(t,n))}},e.cxSelect=function(){return i.apply(this,arguments)},e.cxSelect.defaults={selects:[],url:null,data:null,emptyStyle:null,required:!1,firstTitle:"请选择",firstValue:"",jsonSpace:"",jsonName:"n",jsonValue:"",jsonSub:"s"},e.fn.cxSelect=function(t,n){return this.each(function(i){e.cxSelect(this,t,n)}),this}}),e("cxSelect",{})}),function(e){function t(i){if(n[i])return n[i].exports;var o=n[i]={i:i,l:!1,exports:{}};return e[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};t.m=e,t.c=n,t.d=function(e,n,i){t.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:i})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var o in e)t.d(i,o,function(t){return e[t]}.bind(null,o));return i},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="./",t(t.s=214)}({1:function(e,t,n){"use strict";function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,i)}return n}function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e){return function(e){if(Array.isArray(e))return a(e)}(e)||function(e){if("undefined"!=typeof Symbol&&Symbol.iterator in Object(e))return Array.from(e)}(e)||function(e,t){if(e){if("string"==typeof e)return a(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?a(e,t):void 0}}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function a(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,i=new Array(t);n1&&void 0!==arguments[1]?arguments[1]:100,n=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],i=this,o=!1;return function(){for(var r=arguments.length,a=new Array(r),s=0;s>>0;for(t&&(n=t),i=new Array(a),o=0;o>>0;if("function"!=typeof e)throw new TypeError(e+" is not a function");for(arguments.length>1&&(n=t),i=0;i>>0;if("function"!=typeof e)throw new TypeError;for(var i=[],o=arguments[1],r=0;r>>0,o=arguments[1],r=0;r .xm-tips {\n color: #999999;\n padding: 0 10px;\n position: absolute;\n display: flex;\n height: 100%;\n align-items: center;\n}\nxm-select > .xm-icon {\n display: inline-block;\n overflow: hidden;\n position: absolute;\n width: 0;\n height: 0;\n right: 10px;\n top: 50%;\n margin-top: -3px;\n cursor: pointer;\n border: 6px dashed transparent;\n border-top-color: #C2C2C2;\n border-top-style: solid;\n transition: all 0.3s;\n -webkit-transition: all 0.3s;\n}\nxm-select > .xm-icon-expand {\n margin-top: -9px;\n transform: rotate(180deg);\n}\nxm-select > .xm-label.single-row {\n position: absolute;\n top: 0;\n bottom: 0px;\n left: 0px;\n right: 30px;\n overflow: auto hidden;\n}\nxm-select > .xm-label.single-row .scroll {\n overflow-y: hidden;\n}\nxm-select > .xm-label.single-row .label-content {\n flex-wrap: nowrap;\n white-space: nowrap;\n}\nxm-select > .xm-label.auto-row .label-content {\n flex-wrap: wrap;\n padding-right: 30px !important;\n}\nxm-select > .xm-label.auto-row .xm-label-block > span {\n white-space: unset;\n height: 100%;\n}\nxm-select > .xm-label .scroll .label-content {\n display: flex;\n padding: 3px 10px;\n}\nxm-select > .xm-label .xm-label-block {\n display: flex;\n position: relative;\n padding: 0px 5px;\n margin: 2px 5px 2px 0;\n border-radius: 3px;\n align-items: baseline;\n color: #FFF;\n}\nxm-select > .xm-label .xm-label-block > span {\n display: flex;\n color: #FFF;\n white-space: nowrap;\n}\nxm-select > .xm-label .xm-label-block > i {\n color: #FFF;\n margin-left: 8px;\n font-size: 12px;\n cursor: pointer;\n display: flex;\n}\nxm-select > .xm-label .xm-label-block.disabled {\n background-color: #C2C2C2 !important;\n cursor: no-drop !important;\n}\nxm-select > .xm-label .xm-label-block.disabled > i {\n cursor: no-drop !important;\n}\nxm-select > .xm-body {\n position: absolute;\n left: 0;\n top: 42px;\n padding: 5px 0;\n z-index: 999;\n width: 100%;\n min-width: fit-content;\n border: 1px solid #E6E6E6;\n background-color: #fff;\n border-radius: 2px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12);\n animation-name: xm-upbit;\n animation-duration: 0.3s;\n animation-fill-mode: both;\n}\nxm-select > .xm-body .scroll-body {\n overflow-x: hidden;\n overflow-y: auto;\n}\nxm-select > .xm-body .scroll-body::-webkit-scrollbar {\n width: 8px;\n}\nxm-select > .xm-body .scroll-body::-webkit-scrollbar-track {\n -webkit-border-radius: 2em;\n -moz-border-radius: 2em;\n -ms-border-radius: 2em;\n border-radius: 2em;\n background-color: #FFF;\n}\nxm-select > .xm-body .scroll-body::-webkit-scrollbar-thumb {\n -webkit-border-radius: 2em;\n -moz-border-radius: 2em;\n -ms-border-radius: 2em;\n border-radius: 2em;\n background-color: #C2C2C2;\n}\nxm-select > .xm-body.up {\n top: auto;\n bottom: 42px;\n}\nxm-select > .xm-body.relative {\n position: relative;\n display: block !important;\n top: 0;\n box-shadow: none;\n border: none;\n animation-name: none;\n animation-duration: 0;\n min-width: 100%;\n}\nxm-select > .xm-body .xm-group {\n cursor: default;\n}\nxm-select > .xm-body .xm-group-item {\n display: inline-block;\n cursor: pointer;\n padding: 0 10px;\n color: #999;\n font-size: 12px;\n}\nxm-select > .xm-body .xm-option {\n display: flex;\n align-items: center;\n position: relative;\n padding: 0 10px;\n cursor: pointer;\n}\nxm-select > .xm-body .xm-option-icon {\n color: transparent;\n display: flex;\n border: 1px solid #E6E6E6;\n border-radius: 3px;\n justify-content: center;\n align-items: center;\n}\nxm-select > .xm-body .xm-option-icon.xm-custom-icon {\n color: unset;\n border: unset;\n}\nxm-select > .xm-body .xm-option-icon-hidden {\n margin-right: -10px;\n}\nxm-select > .xm-body .xm-option-icon.xm-icon-danx {\n border-radius: 100%;\n}\nxm-select > .xm-body .xm-option-content {\n display: flex;\n position: relative;\n padding-left: 15px;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n color: #666;\n width: calc(100% - 20px);\n}\nxm-select > .xm-body .xm-option.hide-icon .xm-option-content {\n padding-left: 0;\n}\nxm-select > .xm-body .xm-option.selected.hide-icon .xm-option-content {\n color: #FFF !important;\n}\nxm-select > .xm-body .xm-option .loader {\n width: 0.8em;\n height: 0.8em;\n margin-right: 6px;\n color: #C2C2C2;\n}\nxm-select > .xm-body .xm-select-empty {\n text-align: center;\n color: #999;\n}\nxm-select > .xm-body .disabled {\n cursor: no-drop;\n}\nxm-select > .xm-body .disabled:hover {\n background-color: #FFF;\n}\nxm-select > .xm-body .disabled .xm-option-icon {\n border-color: #C2C2C2 !important;\n}\nxm-select > .xm-body .disabled .xm-option-content {\n color: #C2C2C2 !important;\n}\nxm-select > .xm-body .disabled.selected > .xm-option-icon {\n color: #C2C2C2 !important;\n}\nxm-select > .xm-body .xm-search {\n background-color: #FFF !important;\n position: relative;\n padding: 0 10px;\n margin-bottom: 5px;\n cursor: pointer;\n}\nxm-select > .xm-body .xm-search > i {\n position: absolute;\n color: #666;\n}\nxm-select > .xm-body .xm-search-input {\n border: none;\n border-bottom: 1px solid #E6E6E6;\n padding-left: 27px;\n cursor: text;\n}\nxm-select > .xm-body .xm-paging {\n padding: 0 10px;\n display: flex;\n margin-top: 5px;\n}\nxm-select > .xm-body .xm-paging > span:first-child {\n border-radius: 2px 0 0 2px;\n}\nxm-select > .xm-body .xm-paging > span:last-child {\n border-radius: 0 2px 2px 0;\n}\nxm-select > .xm-body .xm-paging > span {\n display: flex;\n flex: auto;\n justify-content: center;\n vertical-align: middle;\n margin: 0 -1px 0 0;\n background-color: #fff;\n color: #333;\n font-size: 12px;\n border: 1px solid #e2e2e2;\n flex-wrap: nowrap;\n width: 100%;\n overflow: hidden;\n min-width: 50px;\n}\nxm-select > .xm-body .xm-toolbar {\n padding: 0 10px;\n display: flex;\n margin: -3px 0;\n cursor: default;\n}\nxm-select > .xm-body .xm-toolbar .toolbar-tag {\n cursor: pointer;\n display: flex;\n margin-right: 15px;\n color: #666;\n align-items: baseline;\n}\nxm-select > .xm-body .xm-toolbar .toolbar-tag:hover {\n opacity: 0.8;\n}\nxm-select > .xm-body .xm-toolbar .toolbar-tag:active {\n opacity: 1;\n}\nxm-select > .xm-body .xm-toolbar .toolbar-tag > i {\n margin-right: 2px;\n font-size: 14px;\n}\nxm-select > .xm-body .xm-toolbar .toolbar-tag:last-child {\n margin-right: 0;\n}\nxm-select > .xm-body .xm-body-custom {\n line-height: initial;\n cursor: default;\n}\nxm-select > .xm-body .xm-body-custom * {\n box-sizing: initial;\n}\nxm-select > .xm-body .xm-tree {\n position: relative;\n}\nxm-select > .xm-body .xm-tree-icon {\n display: inline-block;\n margin-right: 3px;\n cursor: pointer;\n border: 6px dashed transparent;\n border-left-color: #C2C2C2;\n border-left-style: solid;\n transition: all 0.3s;\n -webkit-transition: all 0.3s;\n z-index: 2;\n visibility: hidden;\n}\nxm-select > .xm-body .xm-tree-icon.expand {\n margin-top: 3px;\n margin-right: 5px;\n margin-left: -2px;\n transform: rotate(90deg);\n}\nxm-select > .xm-body .xm-tree-icon.xm-visible {\n visibility: visible;\n}\nxm-select > .xm-body .xm-tree .left-line {\n position: absolute;\n left: 13px;\n width: 0;\n z-index: 1;\n border-left: 1px dotted #c0c4cc !important;\n}\nxm-select > .xm-body .xm-tree .top-line {\n position: absolute;\n left: 13px;\n height: 0;\n z-index: 1;\n border-top: 1px dotted #c0c4cc !important;\n}\nxm-select > .xm-body .xm-tree .xm-tree-icon + .top-line {\n margin-left: 1px;\n}\nxm-select > .xm-body .scroll-body > .xm-tree > .xm-option > .top-line,\nxm-select > .xm-body .scroll-body > .xm-option > .top-line {\n width: 0 !important;\n}\nxm-select > .xm-body .xm-cascader-box {\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n padding: 5px 0;\n border: 1px solid #E6E6E6;\n background-color: #fff;\n border-radius: 2px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12);\n margin: -1px;\n}\nxm-select > .xm-body .xm-cascader-box::before {\n content: ' ';\n position: absolute;\n width: 0;\n height: 0;\n border: 6px solid transparent;\n border-right-color: #E6E6E6;\n top: 10px;\n left: -12px;\n}\nxm-select > .xm-body .xm-cascader-box::after {\n content: ' ';\n position: absolute;\n width: 0;\n height: 0;\n border: 6px solid transparent;\n border-right-color: #fff;\n top: 10px;\n left: -11px;\n}\nxm-select > .xm-body .xm-cascader-scroll {\n height: 100%;\n overflow-x: hidden;\n overflow-y: auto;\n}\nxm-select > .xm-body.cascader {\n width: unset;\n min-width: unset;\n}\nxm-select > .xm-body.cascader .xm-option-content {\n padding-left: 8px;\n}\nxm-select > .xm-body.cascader .disabled .xm-right-arrow {\n color: #C2C2C2 !important;\n}\nxm-select > .xm-body.cascader .hide-icon.disabled .xm-right-arrow {\n color: #999 !important;\n}\nxm-select .xm-input {\n cursor: pointer;\n border-radius: 2px;\n border-width: 1px;\n border-style: solid;\n border-color: #E6E6E6;\n display: block;\n width: 100%;\n box-sizing: border-box;\n background-color: #FFF;\n line-height: 1.3;\n padding-left: 10px;\n outline: 0;\n user-select: text;\n -ms-user-select: text;\n -moz-user-select: text;\n -webkit-user-select: text;\n}\nxm-select .dis {\n display: none;\n}\nxm-select .loading {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(255, 255, 255, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n}\nxm-select .loader {\n border: 0.2em dotted currentcolor;\n border-radius: 50%;\n -webkit-animation: 1s loader linear infinite;\n animation: 1s loader linear infinite;\n display: inline-block;\n width: 1em;\n height: 1em;\n color: inherit;\n vertical-align: middle;\n pointer-events: none;\n}\nxm-select .xm-select-default {\n position: absolute;\n width: 100%;\n height: 100%;\n border: none;\n visibility: hidden;\n}\nxm-select .xm-select-disabled {\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n cursor: no-drop;\n z-index: 2;\n opacity: 0.3;\n background-color: #FFF;\n}\nxm-select .item--divided {\n border-top: 1px solid #ebeef5;\n width: calc(100% - 20px);\n cursor: initial;\n}\nxm-select .xm-right-arrow {\n position: absolute;\n color: #666;\n right: 5px;\n top: -1px;\n font-weight: 700;\n transform: scale(0.6, 1);\n}\nxm-select .xm-right-arrow::after {\n content: '>';\n}\nxm-select[size='large'] {\n min-height: 40px;\n line-height: 40px;\n}\nxm-select[size='large'] .xm-input {\n height: 40px;\n}\nxm-select[size='large'] .xm-label .scroll .label-content {\n line-height: 34px;\n}\nxm-select[size='large'] .xm-label .xm-label-block {\n height: 30px;\n line-height: 30px;\n}\nxm-select[size='large'] .xm-body .xm-option .xm-option-icon {\n height: 20px;\n width: 20px;\n font-size: 20px;\n}\nxm-select[size='large'] .xm-paging > span {\n height: 34px;\n line-height: 34px;\n}\nxm-select[size='large'] .xm-tree .left-line {\n height: 100%;\n bottom: 20px;\n}\nxm-select[size='large'] .xm-tree .left-line-group {\n height: calc(100% - 40px);\n}\nxm-select[size='large'] .xm-tree .xm-tree-icon.xm-hidden + .top-line {\n top: 19px;\n}\nxm-select[size='large'] .item--divided {\n margin: 10px;\n}\nxm-select {\n min-height: 36px;\n line-height: 36px;\n}\nxm-select .xm-input {\n height: 36px;\n}\nxm-select .xm-label .scroll .label-content {\n line-height: 30px;\n}\nxm-select .xm-label .xm-label-block {\n height: 26px;\n line-height: 26px;\n}\nxm-select .xm-body .xm-option .xm-option-icon {\n height: 18px;\n width: 18px;\n font-size: 18px;\n}\nxm-select .xm-paging > span {\n height: 30px;\n line-height: 30px;\n}\nxm-select .xm-tree .left-line {\n height: 100%;\n bottom: 18px;\n}\nxm-select .xm-tree .left-line-group {\n height: calc(100% - 36px);\n}\nxm-select .xm-tree .xm-tree-icon.xm-hidden + .top-line {\n top: 17px;\n}\nxm-select .item--divided {\n margin: 9px;\n}\nxm-select[size='small'] {\n min-height: 32px;\n line-height: 32px;\n}\nxm-select[size='small'] .xm-input {\n height: 32px;\n}\nxm-select[size='small'] .xm-label .scroll .label-content {\n line-height: 26px;\n}\nxm-select[size='small'] .xm-label .xm-label-block {\n height: 22px;\n line-height: 22px;\n}\nxm-select[size='small'] .xm-body .xm-option .xm-option-icon {\n height: 16px;\n width: 16px;\n font-size: 16px;\n}\nxm-select[size='small'] .xm-paging > span {\n height: 26px;\n line-height: 26px;\n}\nxm-select[size='small'] .xm-tree .left-line {\n height: 100%;\n bottom: 16px;\n}\nxm-select[size='small'] .xm-tree .left-line-group {\n height: calc(100% - 32px);\n}\nxm-select[size='small'] .xm-tree .xm-tree-icon.xm-hidden + .top-line {\n top: 15px;\n}\nxm-select[size='small'] .item--divided {\n margin: 8px;\n}\nxm-select[size='mini'] {\n min-height: 28px;\n line-height: 28px;\n}\nxm-select[size='mini'] .xm-input {\n height: 28px;\n}\nxm-select[size='mini'] .xm-label .scroll .label-content {\n line-height: 22px;\n}\nxm-select[size='mini'] .xm-label .xm-label-block {\n height: 18px;\n line-height: 18px;\n}\nxm-select[size='mini'] .xm-body .xm-option .xm-option-icon {\n height: 14px;\n width: 14px;\n font-size: 14px;\n}\nxm-select[size='mini'] .xm-paging > span {\n height: 22px;\n line-height: 22px;\n}\nxm-select[size='mini'] .xm-tree .left-line {\n height: 100%;\n bottom: 14px;\n}\nxm-select[size='mini'] .xm-tree .left-line-group {\n height: calc(100% - 28px);\n}\nxm-select[size='mini'] .xm-tree .xm-tree-icon.xm-hidden + .top-line {\n top: 13px;\n}\nxm-select[size='mini'] .item--divided {\n margin: 7px;\n}\n.layui-form-pane xm-select {\n margin: -1px -1px -1px 0;\n}\n",""]),e.exports=t},221:function(e,t){(function(t){e.exports=t}).call(this,{})},27:function(e,t,n){"use strict";e.exports=function(e){var t=[];return t.toString=function(){return this.map(function(t){var n=function(e,t){var n=e[1]||"",i=e[3];if(!i)return n;if(t&&"function"==typeof btoa){var o=function(e){return"/*# ".concat("sourceMappingURL=data:application/json;charset=utf-8;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(e)))))," */")}(i);return[n].concat(i.sources.map(function(e){return"/*# sourceURL=".concat(i.sourceRoot||"").concat(e," */")})).concat([o]).join("\n")}return[n].join("\n")}(t,e);return t[2]?"@media ".concat(t[2]," {").concat(n,"}"):n}).join("")},t.i=function(e,n,i){"string"==typeof e&&(e=[[null,e,""]]);var o={};if(i)for(var r=0;r=0&&x.splice(t,1)}function s(e){var t=document.createElement("style");if(void 0===e.attrs.type&&(e.attrs.type="text/css"),void 0===e.attrs.nonce){var i=function(){return n.nc}();i&&(e.attrs.nonce=i)}return l(t,e.attrs),r(e,t),t}function l(e,t){Object.keys(t).forEach(function(n){e.setAttribute(n,t[n])})}function c(e,t){var n,i,o,c;if(t.transform&&e.css){if(!(c="function"==typeof t.transform?t.transform(e.css):t.transform.default(e.css)))return function(){};e.css=c}if(t.singleton){var p=C++;n=b||(b=s(t)),i=d.bind(null,n,p,!1),o=d.bind(null,n,p,!0)}else e.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(n=function(e){var t=document.createElement("link");return void 0===e.attrs.type&&(e.attrs.type="text/css"),e.attrs.rel="stylesheet",l(t,e.attrs),r(e,t),t}(t),i=h.bind(null,n,t),o=function(){a(n),n.href&&URL.revokeObjectURL(n.href)}):(n=s(t),i=u.bind(null,n),o=function(){a(n)});return i(e),function(t){if(t){if(t.css===e.css&&t.media===e.media&&t.sourceMap===e.sourceMap)return;i(e=t)}else o()}}function d(e,t,n,i){var o=n?"":i.css;if(e.styleSheet)e.styleSheet.cssText=A(t,o);else{var r=document.createTextNode(o),a=e.childNodes;a[t]&&e.removeChild(a[t]),a.length?e.insertBefore(r,a[t]):e.appendChild(r)}}function u(e,t){var n=t.css,i=t.media;if(i&&e.setAttribute("media",i),e.styleSheet)e.styleSheet.cssText=n;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(n))}}function h(e,t,n){var i=n.css,o=n.sourceMap,r=void 0===t.convertToAbsoluteUrls&&o;(t.convertToAbsoluteUrls||r)&&(i=I(i)),o&&(i+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(o))))+" */");var a=new Blob([i],{type:"text/css"}),s=e.href;e.href=URL.createObjectURL(a),s&&URL.revokeObjectURL(s)}var p,f,g={},m=(p=function(){return window&&document&&document.all&&!window.atob},function(){return void 0===f&&(f=p.apply(this,arguments)),f}),y=function(e,t){return t?t.querySelector(e):document.querySelector(e)},v=function(e){var t={};return function(e,n){if("function"==typeof e)return e();if(void 0===t[e]){var i=y.call(this,e,n);if(window.HTMLIFrameElement&&i instanceof window.HTMLIFrameElement)try{i=i.contentDocument.head}catch(e){i=null}t[e]=i}return t[e]}}(),b=null,C=0,x=[],I=n(106);e.exports=function(e,t){(t=t||{}).attrs="object"==typeof t.attrs?t.attrs:{},t.singleton||"boolean"==typeof t.singleton||(t.singleton=m()),t.insertInto||(t.insertInto="head"),t.insertAt||(t.insertAt="bottom");var n=o(e,t);return i(n,t),function(e){for(var r=[],a=0;a3)for(n=[n],i=3;i1&&v(o,t,n),t=f(n,o,o,e.__k,null,o.__e,t),"function"==typeof e.type&&(e.__d=t)))}function b(e,t,n,o,r,a,c,d,u){var h,f,g,m,y,b,C,I,w,k,_,S=t.type;if(void 0!==t.constructor)return null;(h=$e.__b)&&h(t);try{e:if("function"==typeof S){if(I=t.props,w=(h=S.contextType)&&o[h.__c],k=h?w?w.props.value:h.__:o,n.__c?C=(f=t.__c=n.__c).__=f.__E:("prototype"in S&&S.prototype.render?t.__c=f=new S(I,k):(t.__c=f=new l(I,k),f.constructor=S,f.render=A),w&&w.sub(f),f.props=I,f.state||(f.state={}),f.context=k,f.__n=o,g=f.__d=!0,f.__h=[]),null==f.__s&&(f.__s=f.state),null!=S.getDerivedStateFromProps&&(f.__s==f.state&&(f.__s=i({},f.__s)),i(f.__s,S.getDerivedStateFromProps(I,f.__s))),m=f.props,y=f.state,g)null==S.getDerivedStateFromProps&&null!=f.componentWillMount&&f.componentWillMount(),null!=f.componentDidMount&&f.__h.push(f.componentDidMount);else{ -if(null==S.getDerivedStateFromProps&&I!==m&&null!=f.componentWillReceiveProps&&f.componentWillReceiveProps(I,k),!f.__e&&null!=f.shouldComponentUpdate&&!1===f.shouldComponentUpdate(I,f.__s,k)||t.__v===n.__v){f.props=I,f.state=f.__s,t.__v!==n.__v&&(f.__d=!1),f.__v=t,t.__e=n.__e,t.__k=n.__k,f.__h.length&&c.push(f),v(t,d,e);break e}null!=f.componentWillUpdate&&f.componentWillUpdate(I,f.__s,k),null!=f.componentDidUpdate&&f.__h.push(function(){f.componentDidUpdate(m,y,b)})}f.context=k,f.props=I,f.state=f.__s,(h=$e.__r)&&h(t),f.__d=!1,f.__v=t,f.__P=e,h=f.render(f.props,f.state,f.context),f.state=f.__s,null!=f.getChildContext&&(o=i(i({},o),f.getChildContext())),g||null==f.getSnapshotBeforeUpdate||(b=f.getSnapshotBeforeUpdate(m,y)),_=null!=h&&h.type==s&&null==h.key?h.props.children:h,p(e,Array.isArray(_)?_:[_],t,n,o,r,a,c,d,u),f.base=t.__e,f.__h.length&&c.push(f),C&&(f.__E=f.__=null),f.__e=!1}else null==a&&t.__v===n.__v?(t.__k=n.__k,t.__e=n.__e):t.__e=x(n.__e,t,n,o,r,a,c,u);(h=$e.diffed)&&h(t)}catch(e){t.__v=null,$e.__e(e,t,n)}return t.__e}function C(e,t){$e.__c&&$e.__c(t,e),e.some(function(t){try{e=t.__h,t.__h=[],e.some(function(e){e.call(t)})}catch(e){$e.__e(e,t.__v)}})}function x(e,t,n,i,o,r,a,s){var l,c,d,u,h,f=n.props,g=t.props;if(o="svg"===t.type||o,null!=r)for(l=0;le.length)&&(t=e.length);for(var n=0,i=new Array(t);ne.length)&&(t=e.length);for(var n=0,i=new Array(t);ne.length)&&(t=e.length);for(var n=0,i=new Array(t);ne.length)&&(t=e.length);for(var n=0,i=new Array(t);no?n-o:o,a=this.labelRef.scrollLeft+e.deltaY;a<0&&(a=0),a>r&&(a=r),this.labelRef.scrollLeft=a}}},{key:"blur",value:function(){var e=this.base.querySelector(".label-search-input");e&&e.blur()}},{key:"labelDrag",value:function(e,t){for(var n=t.type,i=t.target;;){if(!i||"I"===i.tagName)return;if("DIV"===i.tagName&&"fixed"!==i.style.position)break;i=i.parentNode}if(console.log(t),"mousedown"===n){var o=i.cloneNode(!0),r=t.pageX,a=t.pageY,s=t.offsetX,l=t.offsetY;console.log(r,a,s,l),o.style.position="fixed",o.style.left=r-s+"px",o.style.top=a-l+"px",i.appendChild(o),console.log(o),o.onmousemove=function(e){o.style.left=e.pageX-s+"px",o.style.top=e.pageY-l+"px"},o.mouseup=function(){o.parentNode.removeChild(o),o.onmousemove=null,o.mouseup=null,o.mouseleave=null},o.mouseleave=function(){console.log("mouseleave")}}else if("mouseup"===n)for(var c=i.childNodes,d=0;d0&&void 0!==arguments[0]||this.size;var e=this.state.pageIndex;e<=1||(this.changePageIndex(e-1),this.props.pageRemote&&this.postData(e-1,!0))}},{key:"pageNextClick",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.size,t=this.state.pageIndex;t>=e||(this.changePageIndex(t+1),this.props.pageRemote&&this.postData(t+1,!0))}},{key:"changePageIndex",value:function(e){this.setState({pageIndex:e})}},{key:"labelSearch",value:function(e){"input"==e.type?this.searchInput(e):this.handleComposition(e)}},{key:"searchInput",value:function(e){var t=this,n=e.target.value;n!==this.__value&&(this.searchCid&&clearTimeout(this.searchCid),this.inputOver&&(this.__value=n,this.searchCid=setTimeout(function(){t.callback=!0,t.setState({filterValue:t.__value,remote:!0,pageIndex:1})},this.props.delay)))}},{key:"focus",value:function(){this.searchInputRef&&this.searchInputRef.focus()}},{key:"blur",value:function(){this.searchInputRef&&this.searchInputRef.blur()}},{key:"handleComposition",value:function(e){var t=e.type;"compositionstart"===t?(this.inputOver=!1,this.searchCid&&clearTimeout(this.searchCid)):"compositionend"===t&&(this.inputOver=!0,this.searchInput(e))}},{key:"postData",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.state.pageIndex,n=arguments.length>1&&void 0!==arguments[1]&&arguments[1];(this.state.remote||n)&&(this.callback=!1,this.setState({loading:!0,remote:!1}),this.blur(),this.props.remoteMethod(this.state.filterValue,function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:1;setTimeout(function(){e.focus(),e.callback=!0,e.setState({loading:!1,totalSize:n}),e.props.onReset(t,"data")},10)},this.props.show,t))}},{key:"keydown",value:function(e,t){var n=this,i=t.keyCode;if("div"===e&&(27===i||9===i?this.props.onReset(!1,"close"):37===i?this.pagePrevClick():39===i&&this.pageNextClick()),this.props.enableKeyboard){var o=this.props.prop,r=o.value,a=o.optgroup,s=o.disabled,l=this.tempData.filter(function(e){return!e[a]&&!e[s]}),c=l.length-1;if(-1===c)return;var d=l.findIndex(function(e){return e[r]===n.state.val});if(38===i){d<=0?d=c:d>0&&(d-=1);var u=l[d][r];this.setState({val:u}),this.viewTo(u)}else if(40===i){-1===d||d===c?d=0:dj&&(O=j),j>0&&O<=0&&(O=1),!y){var N=(O-1)*e.pageSize,D=N+e.pageSize;_=_.slice(N,D)}var F={cursor:"no-drop",color:"#d2d2d2"},P={},H={};O<=1&&(P=F),O==j&&(H=F),this.state.pageIndex!==O&&this.changePageIndex(O),this.size=j,L=r("div",{class:"xm-paging"},r("span",{style:P,onClick:this.pagePrevClick.bind(this,j)},e.languageProp.paging.prev),r("span",null,this.state.pageIndex," / ",j),r("span",{style:H,onClick:this.pageNextClick.bind(this,j)},e.languageProp.paging.next))}else e.showCount>0&&(_=_.slice(0,e.showCount));var W,V,G=[],Z={__tmp:!0};Z[k]=!0,_.forEach(function(e){var t=$[e.__group__index];delete e.__group__index,W&&!t&&(t=Z),t!=W&&(W=t,t&&G.push(W)),G.push(e)}),_=G,t&&null!=(t=m(this.state.filterValue,Object(De.b)([],_)))&&(V=_).splice.apply(V,[0,0].concat(B((Object(De.d)(t)?t:[t]).map(function(e){return M(M({},e),{},{__node:{}})}))));var z=Object(De.b)([],_);this.tempData=z;var K=r("div",{class:"xm-toolbar"},e.toolbar.list.map(function(t){var i,o=e.languageProp.toolbar[t];i="ALL"===t?{icon:"xm-iconfont xm-icon-quanxuan",name:o,method:function(e){var t=a.optgroup,i=a.disabled,o=e.filter(function(e){return!e[t]}).filter(function(e){return!e[i]}),r=d.filter(function(e){return e[a.disabled]}),s=[];s=c?r.length?r:o.slice(0,1):v>0?r.length>=v?r:Object(De.f)(o.slice(0,v-r.length),r,a):Object(De.f)(o,d,a),n.props.onReset(s,"sels")}}:"CLEAR"===t?{icon:"xm-iconfont xm-icon-qingkong",name:o,method:function(e){n.props.onReset(d.filter(function(e){return e[a.disabled]}),"sels")}}:"REVERSE"===t?{icon:"xm-iconfont xm-icon-fanxuan",name:o,method:function(e){var t=a.optgroup,i=a.disabled,o=e.filter(function(e){return!e[t]}).filter(function(e){return!e[i]}),r=[];d.forEach(function(e){var t=o.findIndex(function(t){return t[I]===e[I]});-1==t?r.push(e):o.splice(t,1)});var s=r.filter(function(e){return e[a.disabled]}),l=[];l=c?s.length?s:o.slice(0,1):v>0?s.length>=v?s:Object(De.f)(o.slice(0,v-s.length),s,a):Object(De.f)(o,r,a),n.props.onReset(l,"sels")}}:t;var s=function(e){"mouseenter"===e.type&&(e.target.style.color=l.color),"mouseleave"===e.type&&(e.target.style.color="")};return r("div",{class:"toolbar-tag",style:{},onClick:function(){Object(De.e)(i.method)&&i.method(z),n.focus()},onMouseEnter:s,onMouseLeave:s},e.toolbar.showIcon&&r("i",{class:i.icon}),r("span",null,i.name))}).filter(function(e){return e})),X="hidden"!=e.model.icon;return(_=_.map(function(t){return t[k]?t.__tmp?r("div",{class:"item--divided"}):r("div",{class:"xm-group"},r("div",{class:"xm-group-item",onClick:n.groupClick.bind(n,t)},t[x])):function(t){var o=!!d.find(function(e){return e[I]==t[I]}),a=o?{color:l.color,border:"none"}:{borderColor:l.color},u={};b&&t[I]===n.state.val&&(u.backgroundColor=l.hover),!X&&o&&(u.backgroundColor=l.color,t[w]&&(u.backgroundColor="#C2C2C2"));var h,p,f=["xm-option",t[w]?" disabled":"",o?" selected":"",X?"show-icon":"hide-icon"].join(" "),g=["xm-option-icon",(h=e.iconfont.select,p=e.iconfont.unselect,(h?!o&&p?p+" xm-custom-icon":h:0)||"xm-iconfont "+(c?"xm-icon-danx":"xm-icon-duox"))].join(" "),m=function(e){"mouseenter"===e.type?t[w]||(b?n.setState({val:t[I]}):e.target.style.backgroundColor=l.hover):"mouseleave"===e.type&&(t[w]||b||(e.target.style.backgroundColor=""))};return r("div",{class:f,style:u,value:t[I],onClick:n.optionClick.bind(n,t,o,t[w]),onMouseEnter:m,onMouseLeave:m},X&&r("i",{class:g,style:a}),r("div",{class:"xm-option-content",dangerouslySetInnerHTML:{__html:s({data:i,item:t,arr:d,name:t[x],value:t[I]})}}))}(t)})).length?C&&this.state.val==We&&this.keydown("div",{keyCode:40}):(!e.pageEmptyShow&&(L=""),_.push(r("div",{class:"xm-select-empty"},u))),r("div",{onClick:this.blockClick,tabindex:"1",style:"outline: none;"},r("div",null,e.toolbar.show&&K,R,r("div",{class:"scroll-body",style:{maxHeight:e.height}},_),e.paging&&L),this.state.loading&&r("div",{class:"loading"},r("span",{class:"loader"})))}},{key:"componentDidMount",value:function(){var e=this.base.querySelector(".xm-search-input");e&&(e.addEventListener("compositionstart",this.handleComposition.bind(this)),e.addEventListener("compositionupdate",this.handleComposition.bind(this)),e.addEventListener("compositionend",this.handleComposition.bind(this)),e.addEventListener("input",this.searchInput.bind(this)),this.searchInputRef=e),this.base.addEventListener("keydown",this.keydown.bind(this,"div"))}},{key:"componentDidUpdate",value:function(){if(this.callback){this.callback=!1;var e=this.props.filterDone;Object(De.e)(e)&&e(this.state.filterValue,this.tempData||[])}}}])&&P(n.prototype,i),t}(l),Ge=function(e){function t(e){return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),o.call(this,e)}!function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&K(e,t)}(t,e);var n,i,o=X(t);return n=t,(i=[{key:"blockClick",value:function(e){e.stopPropagation()}},{key:"shouldComponentUpdate",value:function(){return!this.prepare}},{key:"render",value:function(e){return this.prepare=!0,r("div",{onClick:this.blockClick,class:"xm-body-custom"},r("div",{class:"scroll-body",style:{maxHeight:e.height}},r("div",{style:"margin: 5px 0",dangerouslySetInnerHTML:{__html:e.content}})))}}])&&z(n.prototype,i),t}(l),Ze={},ze=function(e){function t(e){var n;return function(e,t){ -if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),(n=o.call(this,e)).state={expandedKeys:[],filterValue:"",remote:!0,loading:!1,val:Ze},n.searchCid=0,n.inputOver=!0,n.__value="",n.tempData=[],n.__skipAutoExpand="",n}!function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&Q(e,t)}(t,e);var n,i,o=ee(t);return n=t,(i=[{key:"init",value:function(e){var t=e.tree,n=e.dataObj,i=e.flatData,o=e.prop,r=o.value,a=o.optgroup,s=[];!0===t.expandedKeys?s=i.filter(function(e){return!0===e[a]}).map(function(e){return e[r]}):!1===t.expandedKeys||t.expandedKeys.forEach(function(e){s.push(e);for(var t=n[e];t;)!function(){var e=t[r];-1===s.findIndex(function(t){return t===e})&&s.push(e),t=t.__node.parent}()}),this.setState({expandedKeys:s})}},{key:"blockClick",value:function(e){e.stopPropagation()}},{key:"optionClick",value:function(e,t,n,i,o){var r=this;if("line"===i){if(!0===e.__node.loading)return;var a=this.props,s=a.tree,l=a.prop,c=a.sels,d=s.clickExpand,u=s.clickCheck,h=o.target&&Object(De.e)(o.target.getAttribute)&&"expand"===o.target.getAttribute("type");if(d||h){if(!s.lazy&&!e[l.optgroup])return void this.props.ck(e,t,n);var p=e[this.props.prop.value],f=this.state.expandedKeys,g=f.findIndex(function(e){return e===p});-1===g?f.push(p):f.splice(g,1),this.setState({expandedKeys:f});var m=e[l.children];s.lazy&&m&&0===m.length&&!1!==e.__node.loading&&(e.__node.loading=!0,s.load(e,function(t){e.__node.loading=!1,e[l.children]=r.handlerData(t,l.children),e[l.selected]=-1!=c.findIndex(function(t){return t[l.value]===e[l.value]}),r.props.onReset(c,"treeData")}))}else u&&(i="checkbox")}"checkbox"===i&&this.props.ck(e,t,n),this.blockClick(o)}},{key:"handlerData",value:function(e,t){var n=this;return e.map(function(e){return e.__node={},e[t]&&(e[t]=n.handlerData(e[t],t)),e})}},{key:"searchInput",value:function(e){var t=this,n=e.target.value;n!==this.__value&&(clearTimeout(this.searchCid),this.inputOver&&(this.__value=n,this.searchCid=setTimeout(function(){t.callback=!0,t.setState({filterValue:t.__value,remote:!0})},this.props.delay)))}},{key:"focus",value:function(){this.searchInputRef&&this.searchInputRef.focus()}},{key:"blur",value:function(){this.searchInputRef&&this.searchInputRef.blur()}},{key:"handleComposition",value:function(e){var t=e.type;"compositionstart"===t?(this.inputOver=!1,clearTimeout(this.searchCid)):"compositionend"===t&&(this.inputOver=!0,this.searchInput(e))}},{key:"filterData",value:function(e,t,n){var i=this,o=this.props,r=o.prop,a=o.filterMethod,s=o.tree,l=r.children,c=r.optgroup,d=(r.name,r.value);return e.forEach(function(e,o){var u,h=!!t&&!a(t,e,o,r);if(s.strict?u=!1:h=u=!1!==n&&h,e[c]){var p=i.filterData(e[l],t,u),f=!!t&&0===p.filter(function(e){return!e.__node.hidn}).length;if(!(h=(s.strict||u)&&f)&&i.__skipAutoExpand!=t){var g=i.state.expandedKeys;t&&-1===g.findIndex(function(t){return t===e[d]})&&(g.push(e[d]),i.setState({expandedKeys:g})),i.__skipAutoExpand=t}}e.__node.hidn=h}),e}},{key:"postData",value:function(){var e=this;this.state.remote&&(this.callback=!1,this.setState({loading:!0,remote:!1}),this.blur(),this.props.remoteMethod(this.state.filterValue,function(t,n){e.focus(),e.callback=!0,e.setState({loading:!1,totalSize:n}),e.props.onReset(t,"data")},this.props.show,1))}},{key:"componentWillReceiveProps",value:function(e){var t=this;this.props.show!=e.show&&(e.show?setTimeout(function(){return t.focus()},0):(this.setState({filterValue:"",val:Ze}),this.__value="",this.__skipAutoExpand="",this.searchInputRef&&(this.searchInputRef.value="")))}},{key:"componentWillMount",value:function(){this.init(this.props)}},{key:"render",value:function(e,t){function n(e,t){t.forEach(function(t){t[I]?(h.strict||"hidden"===m.parent||e.push(t),n(e,t[x])):e.push(t)})}var i=this,o=(t.expandedKeys,e.prop),a=e.empty,s=e.sels,l=e.theme,c=e.radio,d=e.template,u=e.data,h=e.tree,p=e.filterable,f=e.remoteSearch,g=e.searchTips,m=e.iconfont,y=e.enableKeyboard,v=o.name,b=o.value,C=o.disabled,x=o.children,I=o.optgroup,w="hidden"!=e.model.icon,A=function(e,t,n){var o=!!s.find(function(t){return t[b]==e[b]}),a=e[C],p=!0===e.__node.half;h.strict&&(o=o||p||e.__node.selected,a=a||e.__node.disabled);var f=o?{color:l.color,border:"none"}:{borderColor:l.color},g={paddingLeft:t+"px"};y&&e[b]===i.state.val&&(g.backgroundColor=l.hover),!w&&o&&(g.backgroundColor=l.color,a&&(g.backgroundColor="#C2C2C2"));var I=["xm-option",a?" disabled":"",o?" selected":"",w?"show-icon":"hide-icon"].join(" "),A=0!==n&&"hidden"===m.parent?"xm-option-icon-hidden":["xm-option-icon",(p?m.half?m.half+" xm-custom-icon":0:0!==n&&m.parent?m.parent+" xm-custom-icon":o?m.select?m.select:0:m.unselect?m.unselect+" xm-custom-icon":0)||"xm-iconfont "+(c?"xm-icon-danx":h.strict&&p?"xm-icon-banxuan":"xm-icon-duox")].join(" "),k=["xm-tree-icon",n?"expand":"",e[x]&&(e[x].length>0||h.lazy&&!1!==e.__node.loading)?"xm-visible":"xm-hidden"].join(" "),_=[];h.showFolderIcon&&(_.push(r("i",{class:k,type:"expand"})),h.showLine&&(n&&_.push(r("i",{class:"left-line",style:{left:t-h.indent+3+"px"}})),_.push(r("i",{class:"top-line",style:{left:t-h.indent+3+"px",width:h.indent+(0===n?10:-2)+"px"}}))));var S=function(t){"mouseenter"===t.type?e[C]||(y?i.setState({val:e[b]}):t.target.style.backgroundColor=l.hover):"mouseleave"===t.type&&(e[C]||y||(t.target.style.backgroundColor=""))};return r("div",{class:I,style:g,value:e[b],onClick:i.optionClick.bind(i,e,o,e[C],"line"),onMouseEnter:S,onMouseLeave:S},_,e.__node.loading&&r("span",{class:"loader"}),w&&r("i",{class:A,style:f,onClick:i.optionClick.bind(i,e,o,e[C],"checkbox")}),r("div",{class:"xm-option-content",dangerouslySetInnerHTML:{__html:d({data:u,item:e,arr:s,name:e[v],value:e[b]})}}))};p&&(f?this.postData():this.filterData(u,this.state.filterValue));var k=Object(De.b)([],u),_=Object(De.b)([],s);this.tempData=k;var S=u.map(function(e){return function e(t,n){if(!t.__node.hidn){var o=t[x];if(n+=h.indent,o){var a=-1!==i.state.expandedKeys.findIndex(function(e){return t[b]===e});return 0===o.length&&(a=!1),r("div",{class:"xm-tree"},h.showFolderIcon&&h.showLine&&a&&o.length>0&&r("i",{class:"left-line left-line-group",style:{left:n+3+"px"}}),A(t,n,0===o.length&&(!h.lazy||h.lazy&&!1===t.__node.loading)?0:a),a&&r("div",{class:"xm-tree-box"},o.map(function(t){return e(t,n)})))}return A(t,n,0)}}(e,10-h.indent)}).filter(function(e){return e}),T=r("div",{class:"xm-toolbar"},e.toolbar.list.map(function(t){var a,d=e.languageProp.toolbar[t];a="ALL"===t?{icon:"xm-iconfont xm-icon-quanxuan",name:d,method:function(e){var t=[];n(t,e),t=t.filter(function(e){return!e[C]&&!e.__node.hidn}),i.props.onReset(c?t.slice(0,1):Object(De.f)(t,s,o),"treeData")}}:"CLEAR"===t?{icon:"xm-iconfont xm-icon-qingkong",name:d,method:function(e){i.props.onReset(s.filter(function(e){return e[o.disabled]}),"treeData")}}:"REVERSE"===t?{icon:"xm-iconfont xm-icon-fanxuan",name:d,method:function(e){var t=[];n(t,e),t=t.filter(function(e){return!e[C]&&!e.__node.hidn});var r=[];s.forEach(function(e){var n=t.findIndex(function(t){return t[b]===e[b]});-1==n?r.push(e):t.splice(n,1)}),i.props.onReset(c?r.slice(0,1):Object(De.f)(t,r,o),"treeData")}}:t;var u=function(e){"mouseenter"===e.type&&(e.target.style.color=l.color),"mouseleave"===e.type&&(e.target.style.color="")};return r("div",{class:"toolbar-tag",onClick:function(){Object(De.e)(a.method)&&a.method(k,_)},onMouseEnter:u,onMouseLeave:u},e.toolbar.showIcon&&r("i",{class:a.icon}),r("span",null,a.name))}).filter(function(e){return e})),E=r("div",{class:p?"xm-search":"xm-search dis"},r("i",{class:"xm-iconfont xm-icon-sousuo"}),r("input",{class:"xm-input xm-search-input",placeholder:g}));return S.length||S.push(r("div",{class:"xm-select-empty"},a)),r("div",{onClick:this.blockClick,class:"xm-body-tree"},e.toolbar.show&&T,E,r("div",{class:"scroll-body",style:{maxHeight:e.height}},S),this.state.loading&&r("div",{class:"loading"},r("span",{class:"loader"})))}},{key:"componentDidMount",value:function(){var e=this.base.querySelector(".xm-search-input");e&&(e.addEventListener("compositionstart",this.handleComposition.bind(this)),e.addEventListener("compositionupdate",this.handleComposition.bind(this)),e.addEventListener("compositionend",this.handleComposition.bind(this)),e.addEventListener("input",this.searchInput.bind(this)),this.searchInputRef=e)}},{key:"componentDidUpdate",value:function(){if(this.callback){this.callback=!1;var e=this.props.filterDone;Object(De.e)(e)&&e(this.state.filterValue,this.tempData||[])}}}])&&q(n.prototype,i),t}(l),Ke=function(e){function t(e){var n;return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),(n=o.call(this,e)).state={expand:[]},n}!function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&re(e,t)}(t,e);var n,i,o=ae(t);return n=t,(i=[{key:"blockClick",value:function(e){e.stopPropagation()}},{key:"optionClick",value:function(e,t,n,i,o,r){if("line"===i){if(!e.optgroup&&n)return;if(!0===e.__node.loading)return;var a=this.props,s=a.cascader,l=a.prop;if(a.sels,!s.lazy&&!e[l.optgroup])return void this.props.ck(e,t,n);var c=this.state.expand.slice(0,o+1);c[o]=e[this.props.prop.value],this.setState({expand:c})}else"checkbox"===i&&this.props.ck(e,t,n);this.blockClick(r)}},{key:"componentWillReceiveProps",value:function(e){}},{key:"componentWillMount",value:function(){}},{key:"render",value:function(e,t){var n=this,i=e.prop,o=e.empty,a=e.sels,s=e.theme,l=e.radio,c=e.template,d=e.data,u=e.cascader,h=i.name,p=i.value,f=i.disabled,g=i.children,m=i.optgroup,y="hidden"!=e.model.icon,v=[],b=function t(i,o,b){var C=i[g];o=o+u.indent+6;var x=C&&n.state.expand[b]===i[p];return x&&v.push(r("div",{class:"xm-cascader-box",index:b%4,style:{left:o+"px",width:u.indent+"px"}},r("div",{class:"xm-cascader-scroll scroll-body"},C.map(function(e){return t(e,o,b+1)})))),function(t,i,o,v){var b=!!a.find(function(e){return e[p]==t[p]}),C=t[f],x=!0===t.__node.half;u.strict&&(b=b||x||t.__node.selected,C=C||t.__node.disabled);var I=b?{color:s.color,border:"none"}:{borderColor:s.color},w=t[g]&&t[g].length>0,A={backgroundColor:"transparent"},k=["xm-option",C?" disabled":"",b?" selected":"",y?"show-icon":"hide-icon"].join(" "),_=w&&"hidden"===e.iconfont.parent?"xm-option-icon-hidden":["xm-option-icon",(x?e.iconfont.half?e.iconfont.half+" xm-custom-icon":0:w&&e.iconfont.parent?e.iconfont.parent+" xm-custom-icon":b?e.iconfont.select?e.iconfont.select:0:e.iconfont.unselect?e.iconfont.unselect+" xm-custom-icon":0)||"xm-iconfont "+(l?"xm-icon-danx":u.strict&&x?"xm-icon-banxuan":"xm-icon-duox")].join(" ");t[p]===n.state.val&&(A.backgroundColor=s.hover),!y&&b&&(A.backgroundColor=s.color,C&&(A.backgroundColor="#C2C2C2"));var S={},T={};v&&(S.color=s.color,S.fontWeight=700,T.color=s.color);var E=function(e){"mouseenter"===e.type?t[f]||n.setState({val:t[p]}):"mouseleave"===e.type&&n.setState({val:""})};return r("div",{class:k,style:A,value:t[p],onClick:n.optionClick.bind(n,t,b,C,"line",o),onMouseEnter:E,onMouseLeave:E},y&&r("i",{class:_,style:I,onClick:n.optionClick.bind(n,t,b,C,"checkbox",o)}),r("div",{class:"xm-option-content",style:S,dangerouslySetInnerHTML:{__html:c({data:d,item:t,arr:a,name:t[h],value:t[p]})}}),t[m]&&r("div",{class:"xm-right-arrow",style:T}))}(i,0,b,x)},C=d.map(function(e){return b(e,2,0)}).concat(v).filter(function(e){return e});return C.length||C.push(r("div",{class:"xm-select-empty"},o)),r("div",{onClick:this.blockClick,class:"xm-body-cascader scroll-body",style:{width:u.indent+"px",maxHeight:e.height}},C)}},{key:"componentDidMount",value:function(){this.props.onReset("cascader","class")}}])&&oe(n.prototype,i),t}(l),Xe=function(e){function t(e){var n;return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),n=o.call(this,e),et[e.el]=Ce(n),n.state=n.initState(),n.bodyView=null,n}!function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&ye(e,t)}(t,e);var n,i,o=ve(t);return n=t,(i=[{key:"initState",value:function(){return{data:[],dataObj:{},flatData:[],sels:[],show:!1,tmpColor:"",bodyClass:"",time:0}}},{key:"init",value:function(e,t){var n,i=e.data,o=e.prop,r=e.initValue,a=e.radio,s=e.tree,l=e.cascader;if(t){var c={},d=[];this.load(i,c,d,null,0,r?r.map(function(e){return"object"===ge(e)?e[o.value]:e}):null),n=this.exchangeValue(r||Object.keys(c).filter(function(e){return!0===c[e][o.selected]}),c),a&&n.length>1&&(n=n.slice(0,1),(s.show&&s.strict||l.show&&l.strict)&&this.clearAndReset(i,n,!1)),this.setState({sels:n,dataObj:c,flatData:d})}return this.setState({data:i}),n}},{key:"upDate",value:function(e,t){var n=this.state.dataObj,i=this.props,o=i.prop,r=i.tree,a=i.cascader,s=o.value,l=o.disabled,c=o.children;e.map(function(e){return n["object"===ge(e)?e[s]:e]}).filter(function(e){return e}).forEach(function(e){if(e[l]=!t,r.show&&r.strict||a.show&&a.strict){if(t)for(var n=e;n;)n[l]=!1,n=n.__node.parent;!function e(n){n[l]=!t;var i=n[c];i&&Object(De.d)(i)&&i.forEach(function(t){return e(t)})}(e)}}),this.setState({dataObj:n})}},{key:"exchangeValue",value:function(e){var t=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.state.dataObj,i=this.props,o=i.prop,r=i.tree,a=i.cascader,s=(i.data,o.children),l=o.value,c=e.map(function(e){return"object"===ge(e)?pe(pe({},e),{},{__node:{}}):n[e]}).filter(function(e){return e}),d=de(c);if(r.show&&r.strict||a.show&&a.strict){var u={};u[s]=c,function e(t,n){var i=n[s];i&&Object(De.d)(i)&&i.forEach(function(n){-1===c.findIndex(function(e){return e[l]===n[l]})&&t.push(n),e(t,n)})}(d,u),d=d.filter(function(e){return!0!==e[t.props.prop.optgroup]})}return d}},{key:"value",value:function(e,t,n,i){var o=!(arguments.length>4&&void 0!==arguments[4])||arguments[4];!1!==t&&!0!==t&&(t=this.state.show);var r=this.props,a=r.prop,s=r.tree,l=r.cascader,c=this.exchangeValue(e);if(!this.checkMax(c,c,!0)){if(s.show&&s.strict||l.show&&l.strict){var d=this.state.data;this.clearAndReset(d,c,!1),c=this.init({data:d,prop:a},!0)}this.resetSelectValue(c,i||c,o,n),this.setState({show:t})}}},{key:"clearAndReset",value:function(e,t,n){var i=this,o=this.props.prop,r=o.selected,a=o.disabled,s=o.children,l=o.value;e.forEach(function(e){e[r]=-1!=t.findIndex(function(t){return t[l]===e[l]})||n;var o=e[s];if(o&&Object(De.d)(o)&&o.length>0){i.clearAndReset(o,t,e[r]);var c=o.length,d=o.filter(function(e){return!0===e[r]||!0===e.__node.selected}).length;e.__node.selected=d===c,e.__node.half=d>0&&d0,e.__node.disabled=o.filter(function(e){return!0===e[a]||!0===e.__node.disabled}).length===c}})}},{key:"load",value:function(e,t,n,i){var o=this,r=arguments.length>4&&void 0!==arguments[4]?arguments[4]:0,a=arguments.length>5?arguments[5]:void 0,s=this.props,l=s.prop,c=s.tree,d=s.cascader,u=l.children,h=l.optgroup,p=l.value,f=l.selected,g=l.disabled;e.forEach(function(e){e.__node={parent:i,level:r,loading:e.__node&&e.__node.loading},a&&(delete e[f],a.find(function(t){return t===e[p]})&&(e[f]=!0)),t[e[p]]=e,n.push(e);var s=e[u];if(s&&Object(De.d)(s)){var l=s.length;if(l>0){o.load(s,t,n,e,r+1,a),e[h]=!0,(c.show&&c.strict||d.show&&d.strict)&&(!0===e[f]&&(delete e[f],s.forEach(function(e){return e[f]=!0})),!0===e[g]&&(delete e[g],s.forEach(function(e){return e[g]=!0})));var m=s.filter(function(e){return!0===e[f]||!0===e.__node.selected}).length;e.__node.selected=m===l,e.__node.half=m>0&&m0,e.__node.disabled=s.filter(function(e){return!0===e[g]||!0===e.__node.disabled}).length===l}}})}},{key:"resetSelectValue",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],n=arguments.length>2?arguments[2]:void 0,i=!(arguments.length>3&&void 0!==arguments[3])||arguments[3],o=this.props.on;if(Object(De.e)(o)&&this.prepare&&i){var r=o({arr:e,change:t,isAdd:n});if(Object(De.d)(r))return this.value(r,null,!1)}this.setState({sels:e})}},{key:"updateBorderColor",value:function(e){this.setState({tmpColor:e})}},{key:"treeHandler",value:function(e,t,n,i,o){var r=this,a=this.props.prop,s=a.value,l=(a.selected,a.disabled),c=a.children,d=a.optgroup,u=t[c];if(u.filter(function(e){return!(e[l]||e.__node.disabled)}).forEach(function(t){if(t[d])r.treeHandler(e,t,n,i,o);else{var a=e.findIndex(function(e){return e[s]==t[s]});"del"===i?-1!=a&&(e.splice(a,1),n.push(t)):"half"!==i&&"add"!==i||-1==a&&(e.push(t),n.push(t))}}),o){var h=u.length,p=u.filter(function(t){return-1!==e.findIndex(function(e){return e[s]===t[s]})||!0===t.__node.selected}).length;t.__node.selected=p===h,t.__node.half=p>0&&ps;if(s>0&&l)return this.updateBorderColor(a.maxColor),r&&Object(De.e)(r)&&r(t,e),!0}},{key:"itemClick",value:function(e,t,n,i){var o=this.props,r=(o.theme,o.prop),a=o.radio,s=o.repeat,l=o.clickClose,c=(o.max,o.maxMethod,o.tree),d=o.cascader,u=o.data,h=de(this.state.sels),p=r.value,f=(r.selected,r.disabled,r.children),g=r.optgroup;if(!n){if(e[g]&&(c.show&&c.strict||d.show&&d.strict)){e[f];var m,y=[],v=!0;if(e.__node.selected?(m="del",v=!1):e.__node.half?(m="half",this.treeHandler(h,e,y,m),0===y.length&&(m="del",v=!1)):m="add","half"!=m&&this.treeHandler(h,e,y,m),this.checkMax(y,h))return;h=de(this.state.sels),y=[],this.treeHandler(h,e,y,m,!0),this.resetSelectValue(h,y,v),this.setState({data:this.state.data})}else if(!t||s&&!i){if(this.checkMax(e,h))return;h=a?[e]:[].concat(de(h),[e]),this.clearAndReset(u,h,t),this.resetSelectValue(h,[e],!t)}else{var b=h.findIndex(function(t){return t[p]==e[p]});-1!=b&&(h.splice(b,1),this.resetSelectValue(h,[e],!t))}var C=e.__node.parent;if(C){for(;C;){var x=C[f],I=x.length,w=x.filter(function(e){return-1!==h.findIndex(function(t){return t[p]===e[p]})||!0===e.__node.selected}).length;C.__node.selected=w===I,C.__node.half=w>0&&w0,C=C.__node.parent}this.setState({data:this.state.data})}l&&!i&&this.onClick()}}},{key:"onClick",value:function(e){var t=this;if("relative"!==this.props.model.type)if(this.props.disabled)!1!==this.state.show&&this.setState({show:!1});else{var n=!this.state.show;if(n){if(this.props.show&&0==this.props.show())return;Object.keys(qe).filter(function(e){return e!=t.props.el}).forEach(function(e){return qe[e].closed()})}else{if(this.props.hide&&0==this.props.hide())return;this.bodyView.scroll&&this.bodyView.scroll(0,0)}this.setState({show:n}),e&&e.stopPropagation()}}},{key:"onReset",value:function(e,t){var n=this;if("data"===t){var i=e.filter(function(e){return!0===e[n.props.prop.selected]});this.resetSelectValue(Object(De.f)(i,this.state.sels,this.props.prop),i,!0);var o=[];this.load(e,{},o),this.setState({data:e,flatData:o})}else"sels"===t?this.resetSelectValue(e,e,!0):"append"===t?this.append(e):"delete"===t?this.del(e):"auto"===t?this.auto(e):"treeData"===t?this.value(e,null,!0,!1,!1):"close"===t?this.onClick():"class"===t?this.setState({bodyClass:e}):"labelSearchBlur"===t?this.labelRef.blur(e):"labelSearch"===t&&this.generalRef.labelSearch(e)}},{key:"append",value:function(e){var t=this.exchangeValue(e);this.value(Object(De.f)(t,this.state.sels,this.props.prop),this.props.show,!0,t)}},{key:"del",value:function(e){var t=this.props.prop.value,n=this.state.sels,i=this.exchangeValue(e);i.forEach(function(e){var i=n.findIndex(function(n){return n[t]===e[t]});-1!=i&&n.splice(i,1)}),this.value(n,this.props.show,!0,i,!1)}},{key:"auto",value:function(e){var t=this,n=this.props.prop.value;e.filter(function(e){return-1!=t.state.sels.findIndex(function(t){return t[n]===e[n]})}).length==e.length?this.del(e):this.append(e)}},{key:"changeExpandedKeys",value:function(e){var t=this.props,n=t.tree,i=t.prop,o=this.state,r=o.dataObj,a=o.flatData;n.show&&this.treeRef.init({dataObj:r,flatData:a,prop:i,tree:{expandedKeys:e}})}},{key:"calcPosition",value:function(){if(this.state.show&&"fixed"===this.props.model.type){var e=this.base.getBoundingClientRect();return Date.now()-this.state.time>10&&this.setState({time:Date.now()}),{position:"fixed",left:e.x,top:e.y+e.height+4,width:e.width}}return{}}},{key:"componentWillReceiveProps",value:function(e){this.init(e,e.updateData)}},{key:"componentWillMount",value:function(){this.init(this.props,!0)}},{key:"render",value:function(e,t){var n=this,i=e.theme,o=e.prop,a=(e.radio,e.repeat,e.clickClose,e.on,e.max,e.maxMethod,e.content),s=e.disabled,l=e.tree,c=e.submitConversion,d={borderColor:i.color},u=t.data,h=t.dataObj,p=t.flatData,f=t.sels,g=t.show,m=t.tmpColor,y=t.bodyClass;s&&(g=!1);var v={style:pe(pe({},e.style),g?d:{}),onClick:this.onClick.bind(this),ua:Object(De.a)(),size:e.size,tabindex:1};m&&(v.style.borderColor=m,setTimeout(function(){v.style.borderColor="",n.updateBorderColor("")},300)),o.value;var b=pe(pe({},e),{},{data:u,sels:f,ck:this.itemClick.bind(this),title:f.map(function(e){return e[o.name]}).join(","),onReset:this.onReset.bind(this)}),C=pe(pe({},e),{},{data:u,dataObj:h,flatData:p,sels:f,ck:this.itemClick.bind(this),show:g,onReset:this.onReset.bind(this)}),x=a?r(Ge,C):l.show?r(ze,ce({},C,{ref:function(e){return n.treeRef=e}})):e.cascader.show?r(Ke,C):r(Ve,ce({},C,{ref:function(e){return n.generalRef=e}})),I=this.calcPosition();return r("xm-select",v,r("input",{class:"xm-select-default","lay-verify":e.layVerify,"lay-verType":e.layVerType,"lay-reqText":e.layReqText,name:e.name,value:c(f,o)}),r("i",{class:g?"xm-icon xm-icon-expand":"xm-icon"}),0===f.length&&r("div",{class:"xm-tips"},e.tips),r(He,ce({},b,{ref:function(e){return n.labelRef=e}})),r("div",{class:["xm-body",y,e.model.type,g?"":"dis"].join(" "),style:I,ref:function(e){return n.bodyView=e}},x),s&&r("div",{class:"xm-select-disabled"}))}},{key:"componentDidMount",value:function(){var e=this;this.prepare=!0,this.base.addEventListener("keydown",function(t){13===t.keyCode&&e.onClick(t)}),this.input=this.base.querySelector(".xm-select-default");var t=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver;t&&new t(function(t){t.forEach(function(t){"attributes"==t.type&&"class"===t.attributeName&&-1!==e.input.className.indexOf("layui-form-danger")&&(e.input.className="xm-select-default",e.base.style.borderColor=e.props.theme.maxColor,e.base.scrollIntoView&&e.base.scrollIntoView({behavior:"smooth"}))})}).observe(this.input,{attributes:!0});for(var n=this.base;n;){if("FORM"===n.tagName){var i=n.querySelector('button[type="reset"]');i&&i.addEventListener("click",function(t){e.init(e.props,!0)});break}n=n.parentElement}var o=this.props.done;o&&o()}},{key:"componentDidUpdate",value:function(){var e=this.props,t=e.direction,n=e.model;if("relative"!==n.type&&"fixed"!==n.type){var i=this.base.getBoundingClientRect();if("auto"===t){this.bodyView.style.display="block",this.bodyView.style.visibility="hidden";var o=this.bodyView.getBoundingClientRect().height;this.bodyView.style.display="",this.bodyView.style.visibility="";var r=i.y||i.top||0,a=document.documentElement.clientHeight-r-i.height-20;t=a>o||r0&&void 0!==arguments[0]?arguments[0]:"zn",t=Ue[e]||Ye;return{language:e,languageProp:t,data:[],content:"",name:"select",layVerify:"",layVerType:"",layReqText:"",size:"medium",disabled:!1,initValue:null,create:null,tips:t.tips,empty:t.empty,delay:500,searchTips:t.searchTips,filterable:!1,filterMethod:function(e,t,n,i){return!e||-1!=t[i.name].indexOf(e)},remoteSearch:!1,remoteMethod:function(e,t){t([])},direction:"auto",style:{},height:"200px",autoRow:!1,paging:!1,pageSize:10,pageEmptyShow:!0,pageRemote:!1,radio:!1,repeat:!1,clickClose:!1,max:0,maxMethod:function(e,t){},showCount:0,enableKeyboard:!0,enableHoverFirst:!0,selectedKeyCode:13,toolbar:{show:!1,showIcon:!0,list:["ALL","CLEAR"]},tree:{show:!1,showFolderIcon:!0,showLine:!0,indent:20,expandedKeys:[],strict:!0,lazy:!1,load:null,simple:!1,nodeType:"__node_type",clickExpand:!0,clickCheck:!0},cascader:{show:!1,indent:100,strict:!0},prop:{name:"name",value:"value",selected:"selected",disabled:"disabled",children:"children",optgroup:"optgroup",click:"click"},theme:{color:"#009688",maxColor:"#e54d42",hover:"#f2f2f2"},model:{label:{type:"block",text:{left:"",right:"",separator:", "},block:{showCount:0,showIcon:!0,template:null},count:{template:function(e,t){return"已选中 ".concat(t.length," 项, 共 ").concat(e.length," 项")}}},icon:"show",type:"absolute"},iconfont:{select:"",unselect:"",half:"",parent:""},show:function(){},hide:function(){},template:function(e){e.item,e.sels;var t=e.name;return e.value,t},on:function(e){e.arr,e.item,e.selected},submitConversion:function(e,t){return e.map(function(e){return e[t.value]}).join(",")},done:function(){}}}(e.language),this.update(e)}},{key:"update",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=!!e.data;this.options=Object(De.b)(this.options,e),this.options.__render_success=!1;var n=this.options.dom;if(n){var i=this.options.data||[];if("function"==typeof i&&(i=i(),this.options.data=i),Object(De.d)(i))return k(r(Xe,ke({},this.options,{__update:Date.now(),updateData:t})),n),this.options.__render_success=!0,this;Object(De.k)("data数据必须为数组类型, 不能是".concat("undefined"==typeof data?"undefined":_e(data),"类型"))}else Object(De.k)("没有找到渲染对象: ".concat(e.el,", 请检查"))}},{key:"reset",value:function(){var e=this.options.el;return this.init(Qe[e]),et[e].init(this.options,!0),this}},{key:"opened",value:function(){var e=et[this.options.el];return!e.state.show&&e.onClick(),this}},{key:"closed",value:function(){var e=et[this.options.el];return e.state.show&&e.onClick(),this}},{key:"getValue",value:function(e){var t=this,n=this.options,i=n.tree,o=n.prop,r=n.data,a=et[this.options.el].state.sels,s=a;i.show&&i.strict&&i.simple&&(s=[],Object(De.j)(r,a,s,o));var l=Object(De.c)(s,o.children,["__node"]);return"name"===e?l.map(function(e){return e[t.options.prop.name]}):"nameStr"===e?l.map(function(e){return e[t.options.prop.name]}).join(","):"value"===e?l.map(function(e){return e[t.options.prop.value]}):"valueStr"===e?l.map(function(e){return e[t.options.prop.value]}).join(","):l}},{key:"setValue",value:function(e,t){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if(Object(De.d)(e))return et[this.options.el].value(this.options.radio?e.slice(0,1):e,t,n),this;Object(De.k)("请传入数组结构...")}},{key:"append",value:function(e){if(Object(De.d)(e))return et[this.options.el].append(e),this;Object(De.k)("请传入数组结构...")}},{key:"delete",value:function(e){if(Object(De.d)(e))return et[this.options.el].del(e),this;Object(De.k)("请传入数组结构...")}},{key:"warning",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=e||this.options.theme.maxColor;return!0===t?et[this.options.el].base.style.borderColor=n:et[this.options.el].updateBorderColor(n),this}},{key:"getTreeValue",value:function(e,t){var n=this.options,i=n.tree,o=n.cascader,r=n.prop,a=r.value;if(!i.show&&!o.show)return this.getValue(e);for(var s=et[this.options.el].state.sels,l=[],c=i.nodeType,d=function(e,t){l.find(function(t){return t[a]===e[a]})||((e=we({},e))[c]=t,l.push(e))},u=0;u0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"pid",n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"id",i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"children",o=arguments.length>4&&void 0!==arguments[4]?arguments[4]:0,r={};return safety(e).filter(function(e){var a=e[n],s=e[t],l=r[a],c=r[s];return l&&(e[i]=l[i]),r[a]=e,c||(c=Te({},i,[]),r[s]=c),c.push(e),n==o})}}}}),layui.define(["jquery"],function(e){layui.$;!function(e){"use strict";function t(t){return this.each(function(){var n=e(this),i=n.data(d.dataKey),o=e.extend({},c,n.data(),i&&i.option,"object"==typeof t&&t);i||n.data(d.dataKey,i=new d(this,o))})}function n(t){return e(t).closest("div.sp_container").find("input.sp_input")}function i(){return this.each(function(){var e=n(this),t=e.data(d.dataKey);t&&(t.prop.init_set=!0,t.clearAll(t),t.prop.init_set=!1)})}function o(){return this.each(function(){var e=n(this),t=e.data(d.dataKey);t&&t.elem.hidden.val()&&t.setInitRecord(!0)})}function r(t){return this.each(function(){if(t&&e.isArray(t)){var i=n(this),o=i.data(d.dataKey);o&&(o.clearAll(o),o.option.data=t)}})}function a(t){var i=!1;return this.each(function(){var o=n(this),r=o.data(d.dataKey);r&&("undefined"!==e.type(t)?r.disabled(r,t):i=r.disabled(r))}),i}function s(){var t="";return this.each(function(){var i=n(this),o=i.data(d.dataKey);if(o)if(o.option.multiple){var r=[];o.elem.element_box.find("li.selected_tag").each(function(t,n){r.push(e(n).text())}),t+=r.toString()}else t+=o.elem.combo_input.val()}),t}function l(){var t=[];return this.each(function(){var i=n(this),o=i.data(d.dataKey);if(o)if(o.option.multiple)o.elem.element_box.find("li.selected_tag").each(function(n,i){t.push(e(i).data("dataObj"))});else{var r=o.elem.combo_input.data("dataObj");r&&t.push(r)}}),t}var c={data:void 0,lang:"cn",multiple:!1,pagination:!0, -dropButton:!0,listSize:10,multipleControlbar:!0,maxSelectLimit:0,selectToCloseList:!1,initRecord:void 0,dbTable:"",keyField:"id",showField:"name",selectFields:void 0,andOr:"AND",orderBy:!1,pageSize:10,params:void 0,formatItem:void 0,autoFillResult:!1,autoSelectFirst:!1,noResultClean:!0,selectOnly:!1,inputDelay:.5,isHtml:!0,isTree:!1,eSelect:void 0,eOpen:void 0,eAjaxSuccess:void 0,eTagRemove:void 0,eClear:void 0,method:"GET",verify:"",vertype:"tips"},d=function(e,t){this.setOption(t),this.setLanguage(),this.setCssClass(),this.setProp(),this.setElem(e),this.setButtonAttrDefault(),this.setInitRecord(),this.eDropdownButton(),this.eInput(),this.eWhole()};d.version="2.20",d.dataKey="selectPageObject",d.prototype.setOption=function(t){t.selectFields=t.selectFields||t.showField,t.andOr=t.andOr.toUpperCase(),"AND"!==t.andOr&&"OR"!==t.andOr&&(t.andOr="AND");for(var n=["selectFields"],i=0;i#item_text#',textKey:"#item_text#",valueKey:"#item_value#"},page:{current:"page_num",total:"page_count"},msg:{maxSelectLimit:"max_selected_limit"}}},d.prototype.elementRealSize=function(t,n){var i,o,r,a={absolute:!1,clone:!1,includeMargin:!1,display:"block"},s=a,l=t.eq(0),c=[],d="";i=function(){r=l.parents().addBack().filter(":hidden"),d+="visibility: hidden !important; display: "+s.display+" !important; ",!0===s.absolute&&(d+="position: absolute !important;"),r.each(function(){var t=e(this),n=t.attr("style");c.push(n),t.attr("style",n?n+";"+d:d)})},o=function(){r.each(function(t){var n=e(this),i=c[t];void 0===i?n.removeAttr("style"):n.attr("style",i)})},i();var u=/(outer)/.test(n)?l[n](s.includeMargin):l[n]();return o(),u},d.prototype.setElem=function(t){var n={},i=this.option,o=this.css_class,r=this.message,a=e(t),s=a.outerWidth();s<=0&&(s=this.elementRealSize(a,"outerWidth")),s<150&&(s=150),n.combo_input=a.attr({autocomplete:"off"}).addClass(o.input).wrap("
        "),i.selectOnly&&n.combo_input.prop("readonly",!0),n.container=n.combo_input.parent().addClass(o.container),n.combo_input.prop("disabled")&&(i.multiple?n.container.addClass(o.disabled):n.combo_input.addClass(o.input_off)),n.container.width(s),n.button=e("
        ").addClass(o.button),n.dropdown=e(''),n.clear_btn=e("
        ").html(e("").addClass("sp-iconfont if-close")).addClass(o.clear_btn).attr("title",r.clear),i.dropButton||n.clear_btn.addClass(o.align_right),n.element_box=e("
          ").addClass(o.element_box),i.multiple&&i.multipleControlbar&&(n.control=e("
          ").addClass(o.control_box)),n.result_area=e("
          ").addClass(o.re_area),i.pagination&&(n.navi=e("
          ").addClass("sp_pagination").append("
            ")),n.results=e("
              ").addClass(o.results);var l=n.combo_input.attr("id")||n.combo_input.attr("name"),c=n.combo_input.attr("name")||"selectPage",d=c,u=l;if(n.hidden=e('').attr({name:d,id:u,"lay-verify":this.option.verify,"lay-vertype":this.option.vertype}).val(""),n.combo_input.attr({name:c+"_text",id:l+"_text"}),n.container.append(n.hidden),i.dropButton&&(n.container.append(n.button),n.button.append(n.dropdown)),e(document.body).append(n.result_area),n.result_area.append(n.results),i.pagination&&n.result_area.append(n.navi),i.multiple){i.multipleControlbar&&(n.control.append(''),n.control.append(''),n.control.append(''),n.control_text=e("

              "),n.control.append(n.control_text),n.result_area.prepend(n.control)),n.container.addClass("sp_container_combo"),n.combo_input.addClass("sp_combo_input").before(n.element_box);var h=e("

            • ").addClass("input_box");h.append(n.combo_input),n.element_box.append(h),n.combo_input.attr("placeholder")&&n.combo_input.attr("placeholder_bak",n.combo_input.attr("placeholder"))}this.elem=n},d.prototype.setButtonAttrDefault=function(){this.option.dropButton&&this.elem.button.attr("title",this.message.close_btn)},d.prototype.setInitRecord=function(t){var n=this,i=n.option,o=n.elem,r="";if("undefined"!=e.type(o.combo_input.data("init"))&&(i.initRecord=String(o.combo_input.data("init"))),t||i.initRecord||!o.combo_input.val()||(i.initRecord=o.combo_input.val()),o.combo_input.val(""),t||o.hidden.val(i.initRecord),r=t&&o.hidden.val()?o.hidden.val():i.initRecord)if("object"==typeof i.data){var a=new Array,s=r.split(",");e.each(s,function(e,t){for(var n=0;n1&&(a=[a[0]]),n.afterInit(n,a)}else e.ajax({dataType:"json",type:i.method,url:i.data,data:{searchTable:i.dbTable,searchKey:i.keyField,searchValue:r,orderBy:i.orderBy,showField:i.showField,keyField:i.keyField,keyValue:r,selectFields:i.selectFields,isTree:i.isTree,isHtml:i.isHtml},success:function(t){var o=null;i.eAjaxSuccess&&e.isFunction(i.eAjaxSuccess)&&(o=i.eAjaxSuccess(t)),n.afterInit(n,o.list)},error:function(){n.ajaxErrorNotify(n)},complete:function(t){var n=t.getResponseHeader("__token__");n&&e("input[name='__token__']").val(n)}})},d.prototype.afterInit=function(t,n){if(n&&(!e.isArray(n)||0!==n.length)){e.isArray(n)||(n=[n]);var i=t.option,o=t.css_class,r=function(t){var n=t[i.showField];if(i.formatItem&&e.isFunction(i.formatItem))try{n=i.formatItem(t)}catch(e){}return n};if(i.multiple)t.prop.init_set=!0,t.clearAll(t),e.each(n,function(e,n){var o={text:r(n),value:n[i.keyField]};t.isAlreadySelected(t,o)||t.addNewTag(t,n,o)}),t.tagValuesSet(t),t.inputResize(t),t.prop.init_set=!1;else{var a=n[0];t.elem.combo_input.val(r(a)),t.elem.hidden.val(a[i.keyField]),t.prop.prev_value=r(a),t.prop.selected_text=r(a),i.selectOnly&&t.elem.combo_input.attr("title",t.message.select_ok).removeClass(o.select_ng).addClass(o.select_ok),t.putClearButton()}}},d.prototype.eDropdownButton=function(){var e=this;e.option.dropButton&&e.elem.button.mouseup(function(t){t.stopPropagation(),e.elem.result_area.is(":hidden")&&!e.elem.combo_input.prop("disabled")?e.elem.combo_input.focus():e.hideResults(e)})},d.prototype.eInput=function(){var t=this,n=t.option,i=t.elem,o=t.message,r=function(){t.prop.page_move=!1,t.suggest(t),t.setCssFocusedInput(t)};i.combo_input.keyup(function(e){t.processKey(t,e)}).keydown(function(e){t.processControl(t,e)}).focus(function(e){i.result_area.is(":hidden")&&(e.stopPropagation(),t.prop.first_show=!0,r())}),i.container.on("click.SelectPage","div."+t.css_class.clear_btn,function(i){i.stopPropagation(),t.disabled(t)||(t.clearAll(t,!0),n.eClear&&e.isFunction(n.eClear)&&n.eClear(t))}),i.result_area.on("mousedown.SelectPage",function(e){e.stopPropagation()}),n.multiple&&(n.multipleControlbar&&(i.control.find(".sp_select_all").on("click.SelectPage",function(){t.selectAllLine(t)}).hover(function(){i.control_text.html(o.select_all)},function(){i.control_text.html("")}),i.control.find(".sp_unselect_all").on("click.SelectPage",function(){t.unSelectAllLine(t)}).hover(function(){i.control_text.html(o.unselect_all)},function(){i.control_text.html("")}),i.control.find(".sp_clear_all").on("click.SelectPage",function(){t.clearAll(t,!0)}).hover(function(){i.control_text.html(o.clear_all)},function(){i.control_text.html("")})),i.element_box.on("click.SelectPage",function(t){var n=t.target||t.srcElement;e(n).is("ul")&&i.combo_input.focus()}),i.element_box.on("click.SelectPage","span.tag_close",function(){var i=e(this).closest("li"),o=i.data("dataObj");t.removeTag(t,i),r(),n.eTagRemove&&e.isFunction(n.eTagRemove)&&n.eTagRemove([o])}),t.inputResize(t))},d.prototype.eWhole=function(){var t=this,n=t.css_class,i=function(e){e.elem.combo_input.val(""),e.option.multiple||e.elem.hidden.val(""),e.prop.selected_text=""};e(document.body).off("mousedown.selectPage").on("mousedown.selectPage",function(t){var o=t.target||t.srcElement,r=e(o).closest("div."+n.container);e("div."+n.container+"."+n.container_open).each(function(){if(this!=r[0]){var t=e(this),o=t.find("input."+n.input).data(d.dataKey);if(!o.elem.combo_input.val()&&o.elem.hidden.val()&&!o.option.multiple)return o.prop.current_page=1,i(o),o.hideResults(o),!0;o.elem.results.find("li").not("."+n.message_box).length?o.option.autoFillResult?o.elem.hidden.val()?o.hideResults(o):o.elem.results.find("li.sp_over").length?o.selectCurrentLine(o):o.option.autoSelectFirst?(o.nextLine(o),o.selectCurrentLine(o)):o.hideResults(o):o.hideResults(o):(o.option.noResultClean?i(o):o.option.multiple||o.elem.hidden.val(""),o.hideResults(o))}})})},d.prototype.eResultList=function(){var t=this,n=this.css_class;t.elem.results.children("li").hover(function(){if(t.prop.key_select)return void(t.prop.key_select=!1);e(this).hasClass(n.selected)||e(this).hasClass(n.message_box)||(e(this).addClass(n.select),t.setCssFocusedResults(t))},function(){e(this).removeClass(n.select)}).click(function(i){if(t.prop.key_select)return void(t.prop.key_select=!1);i.preventDefault(),i.stopPropagation(),e(this).hasClass(n.selected)||t.selectCurrentLine(t)})},d.prototype.eScroll=function(){var t=this.css_class;e(window).on("scroll.SelectPage",function(){e("div."+t.container+"."+t.container_open).each(function(){var n=e(this),i=n.find("input."+t.input).data(d.dataKey),o=i.elem.result_area.offset(),r=e(window).scrollTop(),a=e(document).height(),s=e(window).height(),l=i.elem.result_area.outerHeight(),c=o.top+l,u=a>s,h=i.elem.result_area.hasClass("shadowDown");u&&(h?c>s+r&&i.calcResultsSize(i):o.top '+t+"
            • ";e.elem.results.empty().append(n).show(),e.calcResultsSize(e),e.setOpenStatus(e,!0),e.elem.control.hide(),e.option.pagination&&e.elem.navi.hide()}},d.prototype.scrollWindow=function(t,n){var i,o=t.getCurrentLine(t),r=o&&!n?o.offset().top:t.elem.container.offset().top;t.prop.size_li=t.elem.results.children("li:first").outerHeight(),i=t.prop.size_li;var a,s=e(window).height(),l=e(window).scrollTop(),c=l+s-i;if(o.length)if(rs)a=r-l;else{if(!(r>c))return;a=r-c}else r-1&&t.elem.result_area.is(":visible")||e.inArray(n.keyCode,[13,9])>-1&&t.getCurrentLine(t))switch(n.preventDefault(),n.stopPropagation(),n.cancelBubble=!0,n.returnValue=!1,n.keyCode){case 37:n.shiftKey?t.firstPage(t):t.prevPage(t);break;case 38:t.prop.key_select=!0,t.prevLine(t);break;case 39:n.shiftKey?t.lastPage(t):t.nextPage(t);break;case 40:t.elem.results.children("li").length?(t.prop.key_select=!0,t.nextLine(t)):t.suggest(t);break;case 9:t.prop.key_paging=!0,t.selectCurrentLine(t);break;case 13:t.selectCurrentLine(t);break;case 27:t.prop.key_paging=!0,t.hideResults(t)}},d.prototype.abortAjax=function(e){e.prop.xhr&&(e.prop.xhr.abort(),e.prop.xhr=!1)},d.prototype.suggest=function(t){var n,i=e.trim(t.elem.combo_input.val());n=t.option.multiple?i:i&&i===t.prop.selected_text?"":i,n=n.split(/[\s ]+/),t.option.eOpen&&e.isFunction(t.option.eOpen)&&t.option.eOpen.call(t),t.abortAjax(t);var o=t.prop.current_page||1;"object"==typeof t.option.data?t.searchForJson(t,n,o):t.searchForDb(t,n,o)},d.prototype.setLoading=function(e){""===e.elem.results.html()&&e.setOpenStatus(e,!0)},d.prototype.searchForDb=function(t,n,i){var o=t.option;o.eAjaxSuccess&&e.isFunction(o.eAjaxSuccess)||t.hideResults(t);var r=o.params,a={},s=o.selectFields;n.length&&n[0]&&n[0]!==t.prop.prev_value&&(i=1);var l={q_word:n,pageNumber:i,pageSize:o.pageSize,andOr:o.andOr,orderBy:o.orderBy,searchTable:o.dbTable,showField:t.option.showField,keyField:t.option.keyField,selectFields:t.option.selectFields,isTree:t.option.isTree,isHtml:t.option.isHtml};if(!1!==o.orderBy&&(l.orderBy=o.orderBy),l[s]=n[0],r&&e.isFunction(r)){var c=r.call(t);a=c&&e.isPlainObject(c)?e.extend({},l,c):l}else a=l;t.prop.xhr=e.ajax({dataType:"json",url:o.data,type:o.method,data:a,success:function(r){if(!r||!e.isPlainObject(r))return t.hideResults(t),void t.ajaxErrorNotify(t);var a={},s={};try{a=o.eAjaxSuccess(r),s.originalResult=a.list,s.cnt_whole=a.totalRow}catch(e){return void t.showMessage(t,t.message.ajax_error)}if(s.candidate=[],s.keyField=[],"object"!=typeof s.originalResult)return t.prop.xhr=null,void t.notFoundSearch(t);s.cnt_page=s.originalResult.length;for(var l=0;l '),t.append('
            • '),t.append('
            • '+r()+"
            • "),t.append('
            • '),t.append('
            • '),t.show()}else t.find("li.pageInfoBox a").html(r())}(e,r,i,a);var s="disabled",l=r.find("li.csFirstPage"),c=r.find("li.csPreviousPage"),d=r.find("li.csNextPage"),u=r.find("li.csLastPage");1===i||0===i?(l.hasClass(s)||l.addClass(s),c.hasClass(s)||c.addClass(s)):(l.hasClass(s)&&l.removeClass(s),c.hasClass(s)&&c.removeClass(s)),i===a||0===a?(d.hasClass(s)||d.addClass(s),u.hasClass(s)||u.addClass(s)):(d.hasClass(s)&&d.removeClass(s),u.hasClass(s)&&u.removeClass(s)),a>1&&e.ePaging()},d.prototype.displayResults=function(t,n,i){var o=t.option,r=t.elem;if(r.results.hide().empty(),o.multiple&&"number"===e.type(o.maxSelectLimit)&&o.maxSelectLimit>0){var a=r.element_box.find("li.selected_tag").length;if(a>0&&a>=o.maxSelectLimit){var s=t.message.max_selected;return void t.showMessage(t,s.replace(t.template.msg.maxSelectLimit,o.maxSelectLimit))}}if(n.candidate.length)for(var l=n.candidate,c=n.keyField,d=r.hidden.val(),u=d?d.split(","):new Array,h="",p=0;p").html(h).attr({pkey:c[p]});o.formatItem||f.attr("title",h),-1!==e.inArray(c[p].toString(),u)&&f.addClass(t.css_class.selected),f.data("dataObj",n.originalResult[p]),r.results.append(f)}else{var g='
            • '+t.message.not_found+"
            • ";r.results.append(g)}r.results.show(),o.multiple&&o.multipleControlbar&&r.control.show(),o.pagination&&r.navi.show(),t.calcResultsSize(t),t.setOpenStatus(t,!0),t.eResultList(),t.eScroll(),i&&n.candidate.length&&o.autoSelectFirst&&t.nextLine(t)},d.prototype.calcResultsSize=function(t){var n=t.option,i=t.elem,o=function(){if("static"!==i.container.css("position")){var t;if(!n.pagination){t=i.results.find("li:first").outerHeight(!0)*n.listSize,i.results.css({"max-height":t,"overflow-y":"auto"})}var o=e(document).width(),r=e(document).height(),a=e(window).height(),s=i.container.offset(),l=e(window).scrollTop(),c=i.result_area.outerWidth(),d=s.left,u=i.container.outerHeight(),h=s.left+c>o?d-(c-i.container.outerWidth()):d,p=s.top,f=0,g=p+u+t+5,m=r>a;return t=i.result_area.outerHeight(),p-l-5>t&&m&&g>a+l||!m&&g>a?(f=s.top-t-5,i.result_area.removeClass("shadowUp shadowDown").addClass("shadowUp")):(f=s.top+(n.multiple?i.container.outerHeight():u),i.result_area.removeClass("shadowUp shadowDown").addClass("shadowDown"),f+=5),{top:f+"px",left:h+"px"}}var y=i.combo_input.offset();i.result_area.css({top:y.top+i.combo_input.outerHeight()+"px",left:y.left+"px"})};if(i.result_area.is(":visible"))i.result_area.css(o());else{var r=o();i.result_area.css(r).show(1,function(){var e=o();r.top===e.top&&r.left===e.left||i.result_area.css(e)})}},d.prototype.hideResults=function(t){t.prop.key_paging&&(t.scrollWindow(t,!0),t.prop.key_paging=!1),t.setCssFocusedInput(t),t.option.autoFillResult,t.elem.results.empty(),t.elem.result_area.hide(),t.setOpenStatus(t,!1),e(window).off("scroll.SelectPage"),t.abortAjax(t),t.setButtonAttrDefault()},d.prototype.disabled=function(t,n){var i=t.elem;if("undefined"===e.type(n))return i.combo_input.prop("disabled");"boolean"===e.type(n)&&(i.combo_input.prop("disabled",n),n?i.container.addClass(t.css_class.disabled):i.container.removeClass(t.css_class.disabled))},d.prototype.firstPage=function(e){e.prop.current_page>1&&(e.prop.current_page=1,e.prop.page_move=!0,e.suggest(e))},d.prototype.prevPage=function(e){e.prop.current_page>1&&(e.prop.current_page--,e.prop.page_move=!0,e.suggest(e))},d.prototype.nextPage=function(e){e.prop.current_page0&&n.maxSelectLimit===t.elem.element_box.find("li.selected_tag").length)return!1}),n.eSelect&&e.isFunction(n.eSelect)&&n.eSelect(i,t),t.afterAction(t,!0)},d.prototype.unSelectAllLine=function(t){var n=t.option,i=[];t.elem.results.find("li").each(function(n,o){var r=e(o).attr("pkey"),a=t.elem.element_box.find('li.selected_tag[itemvalue="'+r+'"]');a.length&&i.push(a.data("dataObj")),t.removeTag(t,a)}),t.afterAction(t,!0),n.eTagRemove&&e.isFunction(n.eTagRemove)&&n.eTagRemove(i)},d.prototype.clearAll=function(t,n){var i=t.option,o=[];i.multiple?(t.elem.element_box.find("li.selected_tag").each(function(t,n){o.push(e(n).data("dataObj")),n.remove()}),t.elem.element_box.find("li.selected_tag").remove()):e(t.elem.combo_input).removeData("dataObj"),t.reset(t),t.afterAction(t,n),i.multiple?i.eTagRemove&&e.isFunction(i.eTagRemove)&&i.eTagRemove(o):t.elem.clear_btn.remove()},d.prototype.reset=function(e){e.elem.combo_input.val(""),e.elem.hidden.val(""),e.prop.prev_value="",e.prop.selected_text="",e.prop.current_page=1},d.prototype.getCurrentLine=function(e){if(e.elem.result_area.is(":hidden"))return!1;var t=e.elem.results.find("li."+e.css_class.select);return!!t.length&&t},d.prototype.isAlreadySelected=function(t,n){var i=!1;if(n.value){var o=t.elem.hidden.val();if(o){var r=o.split(",");r&&r.length&&-1!=e.inArray(n.value,r)&&(i=!0)}}return i},d.prototype.addNewTag=function(t,n,i){if(t.option.multiple&&n&&i){var o,r=t.template.tag.content;r=r.replace(t.template.tag.textKey,i.text),r=r.replace(t.template.tag.valueKey,i.value),o=e(r),o.data("dataObj",n),t.elem.combo_input.prop("disabled")&&o.find("span.tag_close").hide(),t.elem.combo_input.closest("li").before(o)}},d.prototype.removeTag=function(t,n){var i=e(n).attr("itemvalue"),o=t.elem.hidden.val();if("undefined"!=e.type(i)&&o){var r=o.split(","),a=e.inArray(i.toString(),r);-1!=a&&(r.splice(a,1),t.elem.hidden.val(r.toString()))}e(n).remove(),t.inputResize(t)},d.prototype.tagValuesSet=function(t){if(t.option.multiple){var n=t.elem.element_box.find("li.selected_tag");if(n&&n.length){var i=new Array;e.each(n,function(t,n){var o=e(n).attr("itemvalue");"undefined"!==e.type(o)&&i.push(o)}),i.length&&t.elem.hidden.val(i.join(","))}}},d.prototype.inputResize=function(e){if(e.option.multiple){ -var t=e.elem.combo_input.closest("li"),n=function(e,t){t.removeClass("full_width");var n=e.elem.combo_input.val().length+1,i=.75*n+"em";e.elem.combo_input.css("width",i).removeAttr("placeholder").removeAttr("lay-verify")};0===e.elem.element_box.find("li.selected_tag").length&&e.elem.combo_input.attr("placeholder_bak")?(t.hasClass("full_width")||t.addClass("full_width"),e.elem.combo_input.attr("placeholder",e.elem.combo_input.attr("placeholder_bak")).removeAttr("style").attr("lay-verify",e.option.verify)):n(e,t)}},d.prototype.nextLine=function(e){var t,n=e.getCurrentLine(e);if(n?(t=e.elem.results.children("li").index(n),n.removeClass(e.css_class.select)):t=-1,++t-1){e.elem.results.children("li").eq(t).addClass(e.css_class.select),e.setCssFocusedResults(e)}else e.setCssFocusedInput(e);e.scrollWindow(e,!1)};var u=e.fn.selectPage;e.fn.selectPage=t,e.fn.selectPage.Constructor=d,e.fn.selectPageClear=i,e.fn.selectPageRefresh=o,e.fn.selectPageData=r,e.fn.selectPageDisabled=a,e.fn.selectPageText=s,e.fn.selectPageSelectedData=l,e.fn.selectPage.noConflict=function(){return e.fn.selectPage=u,this}}(window.jQuery),layui.link("/static/plugins/lay-module/selectPage/selectpage.css?v=v2.20"),e("selectPage",$.fn.selectPage)}),define("selectPage",function(){}),define("plugins/require-css/css.min!plugins/lay-module/timePicker/timePicker",[],function(){}),layui.define(["laydate","jquery"],function(e){"use strict";var t=layui.jquery,n=layui.laydate,i=function(){this.v="0.0.3"};i.prototype.render=function(e){var i=t(e.elem),o=e.options.timeStamp||!1,r=e.options.format||"YYYY-MM-DD HH:mm:ss";i.on("click",function(e){if(e.stopPropagation(),t(".timePicker").length>=1)return t(".timePicker").remove(),!1;var a=i.offset().top+i.outerHeight()+"px",s=i.offset().left+"px",l='
              ';l+='
              • 今天
              • 昨天
              • 明天
              • 本周
              • 上周
              • 下周
              • 本月
              • 上月
              • 下月
                季度
              • 本季度
              • 上一季度
              • 下一季度
                年度
              • 本年度
              • 上一年度
              • 下一年度
              自定义
              清除
              确定
              ',l=t(l),t("body").append(l),t('[data-role="display"]').on("click",function(){t(".time-select").css("display","flex"),t(this).find("i").remove()}),n.render({elem:"#sTime",theme:"#4d70ff"}),n.render({elem:"#eTime",theme:"#4d70ff"}),t(".time-info").children().find("li").on("click",function(){t(".time-info").children().find("li").removeClass("active"),t(this).children("input").is(":checked")?t(this).children("input").prop("checked",!1):(t(this).addClass("active"),t(this).children("input").prop("checked",!0))}),t('[data-role="clear"]').on("click",function(){i.val(""),t(".timePicker").remove()}),t('[data-role="sure"]').on("click",function(){var e=t(".time-info").children().find("input:checked").val(),n="",a="";switch(e){case"1":n=Dayjs().startOf("day"),a=Dayjs().endOf("day");break;case"2":n=Dayjs().subtract(1,"days").startOf("day"),a=Dayjs().subtract(1,"days").endOf("day");break;case"3":n=Dayjs().subtract(-1,"days").startOf("day"),a=Dayjs().subtract(-1,"days").endOf("day");break;case"4":n=Dayjs().startOf("week"),a=Dayjs().endOf("week");break;case"5":n=Dayjs().subtract(1,"week").startOf("week"),a=Dayjs().subtract(1,"week").endOf("week");break;case"6":n=Dayjs().subtract(-1,"week").startOf("week"),a=Dayjs().subtract(-1,"week").endOf("week");break;case"7":n=Dayjs().startOf("month"),a=Dayjs().endOf("month");break;case"8":n=Dayjs().subtract(1,"month").startOf("month"),a=Dayjs().subtract(1,"month").endOf("month");break;case"9":n=Dayjs().subtract(-1,"month").startOf("month"),a=Dayjs().subtract(-1,"month").endOf("month");break;case"10":n=Dayjs().startOf("quarter"),a=Dayjs().endOf("quarter");break;case"11":n=Dayjs().subtract(1,"quarter").startOf("quarter"),a=Dayjs().subtract(1,"quarter").endOf("quarter");break;case"12":n=Dayjs().subtract(-1,"quarter").startOf("quarter"),a=Dayjs().subtract(-1,"quarter").endOf("quarter");break;case"13":n=Dayjs().startOf("year"),a=Dayjs().endOf("year");break;case"14":n=Dayjs().subtract(1,"year").startOf("year"),a=Dayjs().subtract(1,"year").endOf("year");break;case"15":n=Dayjs().subtract(-1,"year").startOf("year"),a=Dayjs().subtract(-1,"year").endOf("year");break;default:n=t("#sTime").val(),a=t("#eTime").val()}if(!a||!n)return!1;var s="";if(e)s=o?parseInt(n/1e3)+" - "+parseInt(a/1e3):n.format(r)+" - "+a.format(r);else if(a+=" 23:59:59",o){var n=new Date(n).getTime(),a=new Date(a).getTime();s=n/1e3+" - "+a/1e3}else s=Dayjs(n).format(r)+" - "+Dayjs(a).format(r);i.val(s),t(".timePicker").remove()})})},i.prototype.hide=function(e){t(".timePicker").remove()};var i=new i;t(window).scroll(function(){i.hide()}),e("timePicker",i)}),define("timePicker",["css!plugins/lay-module/timePicker/timePicker.css"],function(){}),layui.define(["jquery"],function(e){function t(e){return"number"==typeof e&&!isNaN(e)}function n(e){return void 0===e}function i(e,n){var i=[];return t(n)&&i.push(n),i.slice.apply(e,i)}function o(e,t){for(var n=arguments.length,o=Array(n>2?n-2:0),r=2;r90?180-n:n)*Math.PI/180,o=Math.sin(i),r=Math.cos(i),a=e.width,s=e.height,l=e.aspectRatio,c=void 0,d=void 0;return t?(c=a/(r+o/l),d=c/l):(c=a*r+s*o,d=a*o+s*r),{width:c,height:d}}function u(e,n,i){var o=x("")[0],r=o.getContext("2d"),a=0,s=0,l=n.naturalWidth,c=n.naturalHeight,u=n.rotate,h=n.scaleX,p=n.scaleY,f=t(h)&&t(p)&&(1!==h||1!==p),g=t(u)&&0!==u,m=g||f,y=l*Math.abs(h||1),v=c*Math.abs(p||1),b=void 0,C=void 0,I=void 0;return f&&(b=y/2,C=v/2),g&&(I=d({width:y,height:v,degree:u}),y=I.width,v=I.height,b=y/2,C=v/2),o.width=y,o.height=v,i.fillColor&&(r.fillStyle=i.fillColor,r.fillRect(0,0,y,v)),m&&(a=-l/2,s=-c/2,r.save(),r.translate(b,C)),g&&r.rotate(u*Math.PI/180),f&&r.scale(h,p),r.imageSmoothingEnabled=!!i.imageSmoothingEnabled,i.imageSmoothingQuality&&(r.imageSmoothingQuality=i.imageSmoothingQuality),r.drawImage(e,Math.floor(a),Math.floor(s),Math.floor(l),Math.floor(c)),m&&r.restore(),o}function h(e,t,n){var i="",o=void 0;for(o=t,n+=t;o=8&&(d=r+a))),d)for(n=t.getUint16(d,s),p=0;pi?3===t?h=o*u:p=i/u:3===t?p=i/u:h=o*u;var f={naturalWidth:c,naturalHeight:d,aspectRatio:u,width:h,height:p};f.left=(i-h)/2,f.top=(o-p)/2,f.oldLeft=f.left,f.oldTop=f.top,e.canvas=f,e.limited=1===t||2===t,e.limitCanvas(!0,!0),e.initialImage=x.extend({},r),e.initialCanvas=x.extend({},f)},limitCanvas:function(e,t){var n=this,i=n.options,o=i.viewMode,r=n.container,a=r.width,s=r.height,l=n.canvas,c=l.aspectRatio,d=n.cropBox,u=n.cropped&&d;if(e){var h=Number(i.minCanvasWidth)||0,p=Number(i.minCanvasHeight)||0;o&&(o>1?(h=Math.max(h,a),p=Math.max(p,s),3===o&&(p*c>h?h=p*c:p=h/c)):h?h=Math.max(h,u?d.width:0):p?p=Math.max(p,u?d.height:0):u&&(h=d.width,p=d.height,p*c>h?h=p*c:p=h/c)),h&&p?p*c>h?p=h/c:h=p*c:h?p=h/c:p&&(h=p*c),l.minWidth=h,l.minHeight=p,l.maxWidth=1/0,l.maxHeight=1/0}if(t)if(o){var f=a-l.width,g=s-l.height;l.minLeft=Math.min(0,f),l.minTop=Math.min(0,g),l.maxLeft=Math.max(0,f),l.maxTop=Math.max(0,g),u&&n.limited&&(l.minLeft=Math.min(d.left,d.left+d.width-l.width),l.minTop=Math.min(d.top,d.top+d.height-l.height),l.maxLeft=d.left,l.maxTop=d.top,2===o&&(l.width>=a&&(l.minLeft=Math.min(0,f),l.maxLeft=Math.max(0,f)),l.height>=s&&(l.minTop=Math.min(0,g),l.maxTop=Math.max(0,g))))}else l.minLeft=-l.width,l.minTop=-l.height,l.maxLeft=a,l.maxTop=s},renderCanvas:function(e){var t=this,n=t.canvas,i=t.image,o=i.rotate,r=i.naturalWidth,a=i.naturalHeight;if(t.rotated){t.rotated=!1;var s=d({width:i.width,height:i.height,degree:o}),l=s.width/s.height,u=1===i.aspectRatio;if(u||l!==n.aspectRatio){if(n.left-=(s.width-n.width)/2,n.top-=(s.height-n.height)/2,n.width=s.width,n.height=s.height,n.aspectRatio=l,n.naturalWidth=r,n.naturalHeight=a,u&&o%90||o%180){var h=d({width:r,height:a,degree:o});n.naturalWidth=h.width,n.naturalHeight=h.height}t.limitCanvas(!0,!1)}}(n.width>n.maxWidth||n.widthn.maxHeight||n.heightn.width?r.height=r.width/i:r.width=r.height*i),e.cropBox=r,e.limitCropBox(!0,!0),r.width=Math.min(Math.max(r.width,r.minWidth),r.maxWidth),r.height=Math.min(Math.max(r.height,r.minHeight),r.maxHeight),r.width=Math.max(r.minWidth,r.width*o),r.height=Math.max(r.minHeight,r.height*o),r.left=n.left+(n.width-r.width)/2,r.top=n.top+(n.height-r.height)/2,r.oldLeft=r.left,r.oldTop=r.top,e.initialCropBox=x.extend({},r)},limitCropBox:function(e,t){var n=this,i=n.options,o=i.aspectRatio,r=n.container,a=r.width,s=r.height,l=n.canvas,c=n.cropBox,d=n.limited;if(e){var u=Number(i.minCropBoxWidth)||0,h=Number(i.minCropBoxHeight)||0,p=Math.min(a,d?l.width:a),f=Math.min(s,d?l.height:s);u=Math.min(u,a),h=Math.min(h,s),o&&(u&&h?h*o>u?h=u/o:u=h*o:u?h=u/o:h&&(u=h*o),f*o>p?f=p/o:p=f*o),c.minWidth=Math.min(u,p),c.minHeight=Math.min(h,f),c.maxWidth=p,c.maxHeight=f}t&&(d?(c.minLeft=Math.max(0,l.left),c.minTop=Math.max(0,l.top),c.maxLeft=Math.min(a,l.left+l.width)-c.width,c.maxTop=Math.min(s,l.top+l.height)-c.height):(c.minLeft=0,c.minTop=0,c.maxLeft=a-c.width,c.maxTop=s-c.height))},renderCropBox:function(){var e=this,t=e.options,n=e.container,i=n.width,o=n.height,r=e.cropBox;(r.width>r.maxWidth||r.widthr.maxHeight||r.heightp&&(m=p/r,f=o*m,g=p),i.css({width:f,height:g}).find("img").css({width:a*m,height:s*m,transform:c(x.extend({translateX:-l*m,translateY:-d*m},t))})}))}},R="undefined"!=typeof window?window.PointerEvent:null,$=R?"pointerdown":"touchstart mousedown",L=R?"pointermove":"touchmove mousemove",j=R?" pointerup pointercancel":"touchend touchcancel mouseup",O={bind:function(){var e=this,t=e.options,n=e.$element,i=e.$cropper;x.isFunction(t.cropstart)&&n.on("cropstart",t.cropstart),x.isFunction(t.cropmove)&&n.on("cropmove",t.cropmove),x.isFunction(t.cropend)&&n.on("cropend",t.cropend),x.isFunction(t.crop)&&n.on("crop",t.crop),x.isFunction(t.zoom)&&n.on("zoom",t.zoom),i.on($,o(e.cropStart,this)),t.zoomable&&t.zoomOnWheel&&i.on("wheel mousewheel DOMMouseScroll",o(e.wheel,this)),t.toggleDragModeOnDblclick&&i.on("dblclick",o(e.dblclick,this)),x(document).on(L,e.onCropMove=o(e.cropMove,this)).on(j,e.onCropEnd=o(e.cropEnd,this)),t.responsive&&x(window).on("resize",e.onResize=o(e.resize,this))},unbind:function(){var e=this,t=e.options,n=e.$element,i=e.$cropper;x.isFunction(t.cropstart)&&n.off("cropstart",t.cropstart),x.isFunction(t.cropmove)&&n.off("cropmove",t.cropmove),x.isFunction(t.cropend)&&n.off("cropend",t.cropend),x.isFunction(t.crop)&&n.off("crop",t.crop),x.isFunction(t.zoom)&&n.off("zoom",t.zoom),i.off($,e.cropStart),t.zoomable&&t.zoomOnWheel&&i.off("wheel mousewheel DOMMouseScroll",e.wheel),t.toggleDragModeOnDblclick&&i.off("dblclick",e.dblclick),x(document).off(L,e.onCropMove).off(j,e.onCropEnd),t.responsive&&x(window).off("resize",e.onResize)}},N=/^(e|w|s|n|se|sw|ne|nw|all|crop|move|zoom)$/,M={resize:function(){var e=this,t=e.options,n=e.$container,i=e.container,o=Number(t.minContainerWidth)||200,r=Number(t.minContainerHeight)||100;if(!e.disabled&&i.width!==o&&i.height!==r){var a=n.width()/i.width;if(1!==a||n.height()!==i.height){var s=void 0,l=void 0;t.restore&&(s=e.getCanvasData(),l=e.getCropBoxData()),e.render(),t.restore&&(e.setCanvasData(x.each(s,function(e,t){s[e]=t*a})),e.setCropBoxData(x.each(l,function(e,t){l[e]=t*a})))}}},dblclick:function(){var e=this;e.disabled||"none"===e.options.dragMode||e.setDragMode(e.$dragBox.hasClass("cropper-crop")?"move":"crop")},wheel:function(e){var t=this,n=e.originalEvent||e,i=Number(t.options.wheelZoomRatio)||.1;if(!t.disabled&&(e.preventDefault(),!t.wheeling)){t.wheeling=!0,setTimeout(function(){t.wheeling=!1},50);var o=1;n.deltaY?o=n.deltaY>0?1:-1:n.wheelDelta?o=-n.wheelDelta/120:n.detail&&(o=n.detail>0?1:-1),t.zoom(-o*i,e)}},cropStart:function(e){var t=this;if(!t.disabled){var n=t.options,i=t.pointers,o=e.originalEvent,a=void 0;o&&o.changedTouches?x.each(o.changedTouches,function(e,t){i[t.identifier]=m(t)}):i[o&&o.pointerId||0]=m(o||e),a=r(i).length>1&&n.zoomable&&n.zoomOnTouch?"zoom":x(e.target).data("action"),N.test(a)&&(t.trigger("cropstart",{originalEvent:o,action:a}).isDefaultPrevented()||(e.preventDefault(),t.action=a,t.cropping=!1,"crop"===a&&(t.cropping=!0,t.$dragBox.addClass("cropper-modal"))))}},cropMove:function(e){var t=this,n=t.action;if(!t.disabled&&n){var i=t.pointers,o=e.originalEvent;e.preventDefault(),t.trigger("cropmove",{originalEvent:o,action:n}).isDefaultPrevented()||(o&&o.changedTouches?x.each(o.changedTouches,function(e,t){x.extend(i[t.identifier],m(t,!0))}):x.extend(i[o&&o.pointerId||0],m(o||e,!0)),t.change(e))}},cropEnd:function(e){var t=this;if(!t.disabled){var n=t.action,i=t.pointers,o=e.originalEvent;o&&o.changedTouches?x.each(o.changedTouches,function(e,t){delete i[t.identifier]}):delete i[o&&o.pointerId||0],n&&(e.preventDefault(),r(i).length||(t.action=""),t.cropping&&(t.cropping=!1,t.$dragBox.toggleClass("cropper-modal",t.cropped&&t.options.modal)),t.trigger("cropend",{originalEvent:o,action:n}))}}},D={change:function(e){var t=this,n=t.options,i=t.pointers,o=i[r(i)[0]],a=t.container,s=t.canvas,l=t.cropBox,c=t.action,d=n.aspectRatio,u=l.width,h=l.height,p=l.left,f=l.top,g=p+u,m=f+h,v=0,b=0,C=a.width,I=a.height,w=!0,A=void 0;!d&&e.shiftKey&&(d=u&&h?u/h:1),t.limited&&(v=l.minLeft,b=l.minTop,C=v+Math.min(a.width,s.width,s.left+s.width),I=b+Math.min(a.height,s.height,s.top+s.height));var k={x:o.endX-o.startX,y:o.endY-o.startY};switch(c){case"all":p+=k.x,f+=k.y;break;case"e":if(k.x>=0&&(g>=C||d&&(f<=b||m>=I))){w=!1;break}g+k.x>C&&(k.x=C-g),u+=k.x,d&&(h=u/d,f-=k.x/d/2),u<0&&(c="w",u=0);break;case"n":if(k.y<=0&&(f<=b||d&&(p<=v||g>=C))){w=!1;break}f+k.y=I))){w=!1;break}p+k.x=0&&(m>=I||d&&(p<=v||g>=C))){w=!1;break}m+k.y>I&&(k.y=I-m),h+=k.y,d&&(u=h*d,p-=k.y*d/2),h<0&&(c="n",h=0);break;case"ne":if(d){if(k.y<=0&&(f<=b||g>=C)){w=!1;break}h-=k.y,f+=k.y,u=h*d}else k.x>=0?gb&&(h-=k.y,f+=k.y):(h-=k.y,f+=k.y);u<0&&h<0?(c="sw",h=0,u=0):u<0?(c="nw",u=0):h<0&&(c="se",h=0);break;case"nw":if(d){if(k.y<=0&&(f<=b||p<=v)){w=!1;break}h-=k.y,f+=k.y,u=h*d,p+=k.y*d}else k.x<=0?p>v?(u-=k.x,p+=k.x):k.y<=0&&f<=b&&(w=!1):(u-=k.x,p+=k.x),k.y<=0?f>b&&(h-=k.y,f+=k.y):(h-=k.y,f+=k.y);u<0&&h<0?(c="se",h=0,u=0):u<0?(c="ne",u=0):h<0&&(c="sw",h=0);break;case"sw":if(d){if(k.x<=0&&(p<=v||m>=I)){w=!1;break}u-=k.x,p+=k.x,h=u/d}else k.x<=0?p>v?(u-=k.x,p+=k.x):k.y>=0&&m>=I&&(w=!1):(u-=k.x,p+=k.x),k.y>=0?m=0&&(g>=C||m>=I)){w=!1;break}u+=k.x,h=u/d}else k.x>=0?g=0&&m>=I&&(w=!1):u+=k.x,k.y>=0?m0?c=k.y>0?"se":"ne":k.x<0&&(p-=u,c=k.y>0?"sw":"nw"),k.y<0&&(f-=h),t.cropped||(t.$cropBox.removeClass("cropper-hidden"),t.cropped=!0,t.limited&&t.limitCropBox(!0,!0))}w&&(l.width=u,l.height=h,l.left=p,l.top=f,t.action=c,t.renderCropBox()),x.each(i,function(e,t){t.startX=t.endX,t.startY=t.endY})}},B={crop:function(){var e=this;e.ready&&!e.disabled&&(e.cropped||(e.cropped=!0,e.limitCropBox(!0,!0),e.options.modal&&e.$dragBox.addClass("cropper-modal"),e.$cropBox.removeClass("cropper-hidden")),e.setCropBoxData(e.initialCropBox))},reset:function(){var e=this;e.ready&&!e.disabled&&(e.image=x.extend({},e.initialImage),e.canvas=x.extend({},e.initialCanvas),e.cropBox=x.extend({},e.initialCropBox),e.renderCanvas(),e.cropped&&e.renderCropBox())},clear:function(){var e=this;e.cropped&&!e.disabled&&(x.extend(e.cropBox,{left:0,top:0,width:0,height:0}),e.cropped=!1,e.renderCropBox(),e.limitCanvas(!0,!0),e.renderCanvas(),e.$dragBox.removeClass("cropper-modal"),e.$cropBox.addClass("cropper-hidden"))},replace:function(e,t){var n=this;!n.disabled&&e&&(n.isImg&&n.$element.attr("src",e),t?(n.url=e,n.$clone.attr("src",e),n.ready&&n.$preview.find("img").add(n.$clone2).attr("src",e)):(n.isImg&&(n.replaced=!0),n.options.data=null,n.load(e)))},enable:function(){var e=this;e.ready&&(e.disabled=!1,e.$cropper.removeClass("cropper-disabled"))},disable:function(){var e=this;e.ready&&(e.disabled=!0,e.$cropper.addClass("cropper-disabled"))},destroy:function(){var e=this,t=e.$element;e.loaded?(e.isImg&&e.replaced&&t.attr("src",e.originalUrl),e.unbuild(),t.removeClass("cropper-hidden")):e.isImg?t.off("load",e.start):e.$clone&&e.$clone.remove(),t.removeData("cropper")},move:function(e,t){var i=this,o=i.canvas;i.moveTo(n(e)?e:o.left+Number(e),n(t)?t:o.top+Number(t))},moveTo:function(e,i){var o=this,r=o.canvas,a=!1;n(i)&&(i=e),e=Number(e),i=Number(i),o.ready&&!o.disabled&&o.options.movable&&(t(e)&&(r.left=e,a=!0),t(i)&&(r.top=i,a=!0),a&&o.renderCanvas(!0))},zoom:function(e,t){var n=this,i=n.canvas;e=Number(e),e=e<0?1/(1-e):1+e,n.zoomTo(i.width*e/i.naturalWidth,t)},zoomTo:function(e,t){var n=this,i=n.options,o=n.pointers,a=n.canvas,s=a.width,l=a.height,c=a.naturalWidth,d=a.naturalHeight;if((e=Number(e))>=0&&n.ready&&!n.disabled&&i.zoomable){var u=c*e,h=d*e,p=void 0;if(t&&(p=t.originalEvent),n.trigger("zoom",{originalEvent:p,oldRatio:s/c,ratio:u/c}).isDefaultPrevented())return;if(p){var f=n.$cropper.offset(),g=o&&r(o).length?b(o):{pageX:t.pageX||p.pageX||0,pageY:t.pageY||p.pageY||0};a.left-=(u-s)*((g.pageX-f.left-a.left)/s),a.top-=(h-l)*((g.pageY-f.top-a.top)/l)}else a.left-=(u-s)/2,a.top-=(h-l)/2;a.width=u,a.height=h,n.renderCanvas(!0)}},rotate:function(e){var t=this;t.rotateTo((t.image.rotate||0)+Number(e))},rotateTo:function(e){var n=this;e=Number(e),t(e)&&n.ready&&!n.disabled&&n.options.rotatable&&(n.image.rotate=e%360,n.rotated=!0,n.renderCanvas(!0))},scale:function(e,i){var o=this,r=o.image,a=!1;n(i)&&(i=e),e=Number(e),i=Number(i),o.ready&&!o.disabled&&o.options.scalable&&(t(e)&&(r.scaleX=e,a=!0),t(i)&&(r.scaleY=i,a=!0),a&&o.renderImage(!0))},scaleX:function(e){var n=this,i=n.image.scaleY;n.scale(e,t(i)?i:1)},scaleY:function(e){var n=this,i=n.image.scaleX;n.scale(t(i)?i:1,e)},getData:function(e){var t=this,n=t.options,i=t.image,o=t.canvas,r=t.cropBox,a=void 0,s=void 0;return t.ready&&t.cropped?(s={x:r.left-o.left,y:r.top-o.top,width:r.width,height:r.height},a=i.width/i.naturalWidth,x.each(s,function(t,n){n/=a,s[t]=e?Math.round(n):n})):s={x:0,y:0,width:0,height:0},n.rotatable&&(s.rotate=i.rotate||0),n.scalable&&(s.scaleX=i.scaleX||1,s.scaleY=i.scaleY||1),s},setData:function(e){var n=this,i=n.options,o=n.image,r=n.canvas,a={},s=void 0,l=void 0,c=void 0;x.isFunction(e)&&(e=e.call(n.element)),n.ready&&!n.disabled&&x.isPlainObject(e)&&(i.rotatable&&t(e.rotate)&&e.rotate!==o.rotate&&(o.rotate=e.rotate,s=!0,n.rotated=s),i.scalable&&(t(e.scaleX)&&e.scaleX!==o.scaleX&&(o.scaleX=e.scaleX,l=!0),t(e.scaleY)&&e.scaleY!==o.scaleY&&(o.scaleY=e.scaleY,l=!0)),s?n.renderCanvas():l&&n.renderImage(),c=o.width/o.naturalWidth,t(e.x)&&(a.left=e.x*c+r.left),t(e.y)&&(a.top=e.y*c+r.top),t(e.width)&&(a.width=e.width*c),t(e.height)&&(a.height=e.height*c),n.setCropBoxData(a))},getContainerData:function(){return this.ready?this.container:{}},getImageData:function(){return this.loaded?this.image:{}},getCanvasData:function(){var e=this,t=e.canvas,n={};return e.ready&&x.each(["left","top","width","height","naturalWidth","naturalHeight"],function(e,i){n[i]=t[i]}),n},setCanvasData:function(e){var n=this,i=n.canvas,o=i.aspectRatio;x.isFunction(e)&&(e=e.call(n.$element)),n.ready&&!n.disabled&&x.isPlainObject(e)&&(t(e.left)&&(i.left=e.left),t(e.top)&&(i.top=e.top),t(e.width)?(i.width=e.width,i.height=e.width/o):t(e.height)&&(i.height=e.height,i.width=e.height*o),n.renderCanvas(!0))},getCropBoxData:function(){var e=this,t=e.cropBox;return e.ready&&e.cropped?{left:t.left,top:t.top,width:t.width,height:t.height}:{}},setCropBoxData:function(e){var n=this,i=n.cropBox,o=n.options.aspectRatio,r=void 0,a=void 0;x.isFunction(e)&&(e=e.call(n.$element)),n.ready&&n.cropped&&!n.disabled&&x.isPlainObject(e)&&(t(e.left)&&(i.left=e.left),t(e.top)&&(i.top=e.top),t(e.width)&&e.width!==i.width&&(r=!0,i.width=e.width),t(e.height)&&e.height!==i.height&&(a=!0,i.height=e.height),o&&(r?i.height=i.width/o:a&&(i.width=i.height*o)),n.renderCropBox())},getCroppedCanvas:function(e){var t=this;if(!t.ready||!window.HTMLCanvasElement)return null;if(x.isPlainObject(e)||(e={}),!t.cropped)return u(t.$clone[0],t.image,e);var n=t.getData(),i=n.width,o=n.height,r=i/o,a=void 0,s=void 0,l=void 0;x.isPlainObject(e)&&(a=e.width,s=e.height,a?(s=a/r,l=a/i):s&&(a=s*r,l=s/o));var c=Math.floor(a||i),d=Math.floor(s||o),h=x("")[0],p=h.getContext("2d");h.width=c,h.height=d,e.fillColor&&(p.fillStyle=e.fillColor,p.fillRect(0,0,c,d));var f=function(){var r=u(t.$clone[0],t.image,e),a=r.width,s=r.height,c=t.canvas,d=[r],h=n.x+c.naturalWidth*(Math.abs(n.scaleX||1)-1)/2,p=n.y+c.naturalHeight*(Math.abs(n.scaleY||1)-1)/2,f=void 0,g=void 0,m=void 0,y=void 0,v=void 0,b=void 0;return h<=-i||h>a?(h=0,f=0,m=0,v=0):h<=0?(m=-h,h=0,v=Math.min(a,i+h),f=v):h<=a&&(m=0,v=Math.min(i,a-h),f=v),f<=0||p<=-o||p>s?(p=0,g=0,y=0,b=0):p<=0?(y=-p,p=0,b=Math.min(s,o+p),g=b):p<=s&&(y=0,b=Math.min(o,s-p),g=b),d.push(Math.floor(h),Math.floor(p),Math.floor(f),Math.floor(g)),l&&(m*=l,y*=l,v*=l,b*=l),v>0&&b>0&&d.push(Math.floor(m),Math.floor(y),Math.floor(v),Math.floor(b)),d}();return p.imageSmoothingEnabled=!!e.imageSmoothingEnabled,e.imageSmoothingQuality&&(p.imageSmoothingQuality=e.imageSmoothingQuality),p.drawImage.apply(p,v(f)),h},setAspectRatio:function(e){var t=this,i=t.options;t.disabled||n(e)||(i.aspectRatio=Math.max(0,e)||NaN,t.ready&&(t.initCropBox(),t.cropped&&t.renderCropBox()))},setDragMode:function(e){var t=this,n=t.options,i=void 0,o=void 0;t.loaded&&!t.disabled&&(i="crop"===e,o=n.movable&&"move"===e,e=i||o?e:"none",t.$dragBox.data("action",e).toggleClass("cropper-crop",i).toggleClass("cropper-move",o),n.cropBoxMovable||t.$face.data("action",e).toggleClass("cropper-crop",i).toggleClass("cropper-move",o))}},F=function(){function e(e,t){for(var n=0;n1)switch(t.url=g(e),i){case 2:a=-1;break;case 3:r=-180;break;case 4:s=-1;break;case 5:r=90,s=-1;break;case 6:r=90;break;case 7:r=90,a=-1;break;case 8:r=-90}n.rotatable&&(o.rotate=r),n.scalable&&(o.scaleX=a,o.scaleY=s),t.clone()}},{key:"clone",value:function(){var e=this,t=e.options,n=e.$element,i=e.url,o="",r=void 0;t.checkCrossOrigin&&a(i)&&(o=n.prop("crossOrigin"),o?r=i:(o="anonymous",r=s(i))),e.crossOrigin=o,e.crossOriginUrl=r;var l=document.createElement("img");o&&(l.crossOrigin=o),l.src=r||i;var c=x(l);e.$clone=c,e.isImg?n[0].complete?e.start():n.one("load",x.proxy(e.start,this)):c.one("load",x.proxy(e.start,this)).one("error",x.proxy(e.stop,this)).addClass("cropper-hide").insertAfter(n)}},{key:"start",value:function(){var e=this,t=e.$clone,n=e.$element;e.isImg||(t.off("error",e.stop),n=t),l(n[0],function(t,n){x.extend(e.image,{naturalWidth:t,naturalHeight:n,aspectRatio:t/n}),e.loaded=!0,e.build()})}},{key:"stop",value:function(){var e=this;e.$clone.remove(),e.$clone=null}},{key:"build",value:function(){var e=this,t=e.options,n=e.$element,i=e.$clone;if(e.loaded){e.ready&&e.unbuild();var o=x('
              '),r=o.find(".cropper-crop-box"),a=r.find(".cropper-face");e.$container=n.parent(),e.$cropper=o,e.$canvas=o.find(".cropper-canvas").append(i),e.$dragBox=o.find(".cropper-drag-box"),e.$cropBox=r,e.$viewBox=o.find(".cropper-view-box"),e.$face=a,n.addClass("cropper-hidden").after(o),e.isImg||i.removeClass("cropper-hide"),e.initPreview(),e.bind(),t.aspectRatio=Math.max(0,t.aspectRatio)||NaN,t.viewMode=Math.max(0,Math.min(3,Math.round(t.viewMode)))||0,e.cropped=t.autoCrop,t.autoCrop?t.modal&&e.$dragBox.addClass("cropper-modal"):r.addClass("cropper-hidden"),t.guides||r.find(".cropper-dashed").addClass("cropper-hidden"),t.center||r.find(".cropper-center").addClass("cropper-hidden"),t.cropBoxMovable&&a.addClass("cropper-move").data("action","all"),t.highlight||a.addClass("cropper-invisible"),t.background&&o.addClass("cropper-bg"),t.cropBoxResizable||r.find(".cropper-line, .cropper-point").addClass("cropper-hidden"),e.setDragMode(t.dragMode),e.render(),e.ready=!0,e.setData(t.data),e.completing=setTimeout(function(){x.isFunction(t.ready)&&n.one("ready",t.ready),e.trigger("ready"),e.trigger("crop",e.getData()),e.completed=!0},0)}}},{key:"unbuild",value:function(){var e=this;e.ready&&(e.completed||clearTimeout(e.completing),e.ready=!1,e.completed=!1,e.initialImage=null,e.initialCanvas=null,e.initialCropBox=null,e.container=null,e.canvas=null,e.cropBox=null,e.unbind(),e.resetPreview(),e.$preview=null,e.$viewBox=null,e.$cropBox=null,e.$dragBox=null,e.$canvas=null,e.$container=null,e.$cropper.remove(),e.$cropper=null)}}],[{key:"setDefaults",value:function(e){x.extend(I,x.isPlainObject(e)&&e)}}]),e}();x.extend(W.prototype,T),x.extend(W.prototype,E),x.extend(W.prototype,O),x.extend(W.prototype,M),x.extend(W.prototype,D),x.extend(W.prototype,B);var V=x.fn.cropper;x.fn.cropper=function(e){for(var t=arguments.length,n=Array(t>1?t-1:0),i=1;i\n
              \n
              \n \n \n
              \n
              300x300px,大小2M
              \n
              \n
              \n
              \n
              \n \n
              \n
              \n
              \n
              \n
              \n
              \n
              \n
              \n
              \n
              \n
              \n \n \n
              \n
              \n \n \n \n \n \n
              ';0==t(elem_this).length&&t("body").append(c),layui.form.render();var d=t(elem_this),u=t(elem_this+" .readyimg img"),h=elem_this+" .img-preview",p={aspectRatio:r,preview:h,viewMode:1};n.on("click",function(){winHeight=t(document).height();var e=layui.layer.open({type:1,title:"裁剪",content:d,maxmin:!0,area:a,move:!0,shadeClose:!0,scoll:!0,resize:!0,success:function(e){u.cropper(p)},cancel:function(e){d.css("display","none"),u.cropper("destroy"),layer.close(e)},end:function(){d.css("display","none"),u.cropper("destroy"),layer.close(e)},full:function(e,t){},restore:function(e,t){}})}),t(elem_this+" .layui-btn").on("click",function(){var e=t(this).attr("cropper-event"),n=t(this).parents(".croppersbox").find(".readyimg img").attr("src");if("confirmSave"===e)return!!n&&(u.cropper("getCroppedCanvas",{width:i,height:o}).toBlob(function(e){var n=new FormData;n.append("file",e,"fun-avatar.png"),t.ajax({method:"post",url:s,data:n,processData:!1,contentType:!1,success:function(e){return e.code>0&&(layer.closeAll("page"),t(".croppersbox").hide()),l(e)}})}),!1);if("rotate"===e){if(!n)return!1;var r=t(this).data("option");u.cropper("rotate",r)}else if("reset"===e){if(!n)return!1;u.cropper("reset")}else if("large"===e){if(!n)return!1;u.cropper("zoom",.1)}else if("small"===e){if(!n)return!1;u.cropper("zoom",-.1)}else if("setDragMode"===e){if(!n)return!1;u.cropper("setDragMode","move")}else if("setDragMode1"===e){if(!n)return!1;u.cropper("setDragMode","crop")}t(this).next("input[name='file']").change(function(){var e=new FileReader,t=this.files[0];e.readAsDataURL(t),e.onload=function(e){u.cropper("destroy").attr("src",this.result).cropper(p)}})})}})}),define("croppers",["plugins/lay-module/cropper/cropper","css!plugins/lay-module/cropper/cropper.css"],function(e){return function(){return e.cropper}}(this)),define("plugins/require-css/css.min!plugins/lay-module/autoComplete/autoComplete",[],function(){});var e,t;e=this,t=function(){"use strict";function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,i)}return n}function t(t){for(var n=1;ne.length)&&(t=e.length);for(var n=0,i=new Array(t);n=n}(s=e.query?e.query(s):s,e.trigger,e.threshold)?m(e,s).then(function(n){try{return e.feedback instanceof Error?o():(y(s,e),e.resultsList&&function(e){var n=e.resultsList,i=e.list,o=e.resultItem,r=e.feedback,a=r.matches,s=r.results;if(e.cursor=-1,i.innerHTML="",a.length||n.noResults){var l=new DocumentFragment;s.forEach(function(e,n){var i=u(o.tag,t({id:"".concat(o.id,"_").concat(n),role:"option",innerHTML:e.match,inside:l},o.class&&{class:o.class}));o.element&&o.element(i,e)}),i.append(l),n.element&&n.element(i,r),I(e)}else w(e)}(e),a.call(i))}catch(e){return r(e)}},r):(w(e),a.call(i))})}function l(e){var n=this;return new Promise(function(i,o){function r(){return f("init",e),i()}var a,s,l;return a=e.placeHolder,l={role:"combobox","aria-owns":(s=e.resultsList).id,"aria-haspopup":!0,"aria-expanded":!1},u(e.input,t(t({"aria-controls":s.id,"aria-autocomplete":"both"},a&&{placeholder:a}),!e.wrapper&&t({},l))),e.wrapper&&(e.wrapper=u("div",t({around:e.input,class:e.name+"_wrapper"},l))),s&&(e.list=u(s.tag,t({dest:[s.destination,s.position],id:s.id,role:"listbox",hidden:"hidden"},s.class&&{class:s.class}))),E(e),e.data.cache?m(e).then(function(e){try{return r.call(n)}catch(e){return o(e)}},o):r.call(n)})}function c(e){var t=e.prototype;t.init=function(){l(this)},t.start=function(e){s(this,e)},t.unInit=function(){if(this.wrapper){var e=this.wrapper.parentNode;e.insertBefore(this.input,this.wrapper),e.removeChild(this.wrapper)}var t;T((t=this).events,function(e,n){t[e].removeEventListener(n,t.events[e][n])})},t.open=function(){I(this)},t.close=function(){w(this)},t.goTo=function(e){A(e,this)},t.next=function(){k(this)},t.previous=function(){_(this)},t.select=function(e){S(this,null,e)},t.search=function(e,t,n){return g(e,t,n)}}var d=function(e){return"string"==typeof e?document.querySelector(e):e()},u=function(e,t){var n="string"==typeof e?document.createElement(e):e;for(var i in t){var o=t[i];if("inside"===i)o.append(n);else if("dest"===i)d(o[0]).insertAdjacentElement(o[1],n);else if("around"===i){var r=o;r.parentNode.insertBefore(n,r),n.append(r),null!=r.getAttribute("autofocus")&&r.focus()}else i in n?n[i]=o:n.setAttribute(i,o)}return n},h=function(e,t){return e=String(e).toLowerCase(),t?e.normalize("NFD").replace(/[\u0300-\u036f]/g,"").normalize("NFC"):e},p=function(e,n){return u("mark",t({innerHTML:e},"string"==typeof n&&{class:n})).outerHTML},f=function(e,t){t.input.dispatchEvent(new CustomEvent(e,{bubbles:!0,detail:t.feedback,cancelable:!0}))},g=function(e,t,n){var i=n||{},o=i.mode,r=i.diacritics,a=i.highlight,s=h(t,r);if(t=String(t),e=h(e,r),"loose"===o){var l=(e=e.replace(/ /g,"")).length,c=0,d=Array.from(t).map(function(t,n){return c=e.length?{done:!0}:{done:!1,value:e[i++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,s=!0,l=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){l=!0,a=e},f:function(){try{s||null==n.return||n.return()}finally{if(l)throw a}}}}(n.keys);try{for(d.s();!(c=d.n()).done;)l(c.value)}catch(e){d.e(e)}finally{d.f()}}else l()}),n.filter&&(o=n.filter(o));var a=o.slice(0,t.resultsList.maxResults);t.feedback={query:e,matches:o,results:a},f("results",t)},v="aria-expanded",b="aria-activedescendant",C="aria-selected",x=function(e,n){e.feedback.selection=t({index:n},e.feedback.results[n])},I=function(e){e.isOpen||((e.wrapper||e.input).setAttribute(v,!0),e.list.removeAttribute("hidden"),e.isOpen=!0,f("open",e))},w=function(e){e.isOpen&&((e.wrapper||e.input).setAttribute(v,!1),e.input.setAttribute(b,""),e.list.setAttribute("hidden",""),e.isOpen=!1,f("close",e))},A=function(e,t){var n=t.resultItem,i=t.list.getElementsByTagName(n.tag),r=!!n.selected&&n.selected.split(" ");if(t.isOpen&&i.length){var a,s,l=t.cursor;e>=i.length&&(e=0),e<0&&(e=i.length-1),t.cursor=e,l>-1&&(i[l].removeAttribute(C),r&&(s=i[l].classList).remove.apply(s,o(r))),i[e].setAttribute(C,!0),r&&(a=i[e].classList).add.apply(a,o(r)),t.input.setAttribute(b,i[t.cursor].id),t.list.scrollTop=i[e].offsetTop-t.list.clientHeight+i[e].clientHeight+5,t.feedback.cursor=t.cursor,x(t,e),f("navigate",t)}},k=function(e){A(e.cursor+1,e)},_=function(e){A(e.cursor-1,e)},S=function(e,t,n){(n=n>=0?n:e.cursor)<0||(e.feedback.event=t,x(e,n),f("selection",e),w(e))},T=function(e,t){for(var n in e)for(var i in e[n])t(n,i)},E=function(e){var n,i,o,r=e.events,a=(n=function(){return s(e)},i=e.debounce,function(){clearTimeout(o),o=setTimeout(function(){return n()},i)}),l=e.events=t({input:t({},r&&r.input)},e.resultsList&&{list:r?t({},r.list):{}}),c={input:{input:function(){a()},keydown:function(t){!function(e,t){switch(e.keyCode){case 40:case 38:e.preventDefault(),40===e.keyCode?k(t):_(t);break;case 13:t.submit||e.preventDefault(),t.cursor>=0&&S(t,e);break;case 9:t.resultsList.tabSelect&&t.cursor>=0&&S(t,e);break;case 27:t.input.value="",w(t)}}(t,e)},blur:function(){w(e)}},list:{mousedown:function(e){e.preventDefault()},click:function(t){!function(e,t){var n=t.resultItem.tag.toUpperCase(),i=Array.from(t.list.querySelectorAll(n)),o=e.target.closest(n);o&&o.nodeName===n&&S(t,e,i.indexOf(o))}(t,e)}}};T(c,function(t,n){(e.resultsList||"input"===n)&&(l[t][n]||(l[t][n]=c[t][n]))}),T(l,function(t,n){e[t].addEventListener(n,l[t][n])})};return function e(t){this.options=t,this.id=e.instances=(e.instances||0)+1,this.name="autoComplete",this.wrapper=1,this.threshold=1,this.debounce=0,this.resultsList={position:"afterend",tag:"ul",maxResults:5},this.resultItem={tag:"li"},function(e){var t=e.name,i=e.options,o=e.resultsList,r=e.resultItem;for(var a in i)if("object"===n(i[a]))for(var s in e[a]||(e[a]={}),i[a])e[a][s]=i[a][s];else e[a]=i[a];e.selector=e.selector||"#"+t,o.destination=o.destination||e.selector,o.id=o.id||t+"_list_"+e.id,r.id=r.id||t+"_result",e.input=d(e.selector)}(this),c.call(this,e),l(this)}},"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define("autoComplete",t):(e="undefined"!=typeof globalThis?globalThis:e||self).autoComplete=t(),function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define("Sortable",t):(e=e||self).Sortable=t()}(this,function(){"use strict";function e(e,t){var n,i=Object.keys(e);return Object.getOwnPropertySymbols&&(n=Object.getOwnPropertySymbols(e),t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),i.push.apply(i,n)),i}function t(t){for(var n=1;ne.length)&&(t=e.length);for(var n=0,i=new Array(t);n"===t[0]&&(t=t.substring(1)),e))try{if(e.matches)return e.matches(t);if(e.msMatchesSelector)return e.msMatchesSelector(t);if(e.webkitMatchesSelector)return e.webkitMatchesSelector(t)}catch(e){return}}function u(e,t,n,i){if(e){n=n||document;do{if(null!=t&&(">"!==t[0]||e.parentNode===n)&&d(e,t)||i&&e===n)return e}while(e!==n&&(e=(o=e).host&&o!==document&&o.host.nodeType?o.host:o.parentNode))}var o;return null}function h(e,t,n){var i;e&&t&&(e.classList?e.classList[n?"add":"remove"](t):(i=(" "+e.className+" ").replace(Se," ").replace(" "+t+" "," "),e.className=(i+(n?" "+t:"")).replace(Se," ")))}function p(e,t,n){var i=e&&e.style;if(i){if(void 0===n)return document.defaultView&&document.defaultView.getComputedStyle?n=document.defaultView.getComputedStyle(e,""):e.currentStyle&&(n=e.currentStyle),void 0===t?n:n[t];i[t=t in i||-1!==t.indexOf("webkit")?t:"-webkit-"+t]=n+("string"==typeof n?"":"px")}}function f(e,t){var n="";if("string"==typeof e)n=e;else do{var i=p(e,"transform")}while(i&&"none"!==i&&(n=i+" "+n),!t&&(e=e.parentNode));var o=window.DOMMatrix||window.WebKitCSSMatrix||window.CSSMatrix||window.MSCSSMatrix;return o&&new o(n)}function g(e,t,n){if(e){var i=e.getElementsByTagName(t),o=0,r=i.length;if(n)for(;o=n.left-t&&o<=n.right+t,t=r>=n.top-t&&r<=n.bottom+t;return i&&t?a=e:void 0}}),a);if(t){var n,i={};for(n in e)e.hasOwnProperty(n)&&(i[n]=e[n]);i.target=i.rootEl=t,i.preventDefault=void 0,i.stopPropagation=void 0,t[Te]._onDragOver(i)}}var o,r,a}function N(e){Y&&Y.parentNode[Te]._isOutsideThisEl(e.target)}function M(e,t){if(!e||!e.nodeType||1!==e.nodeType)throw"Sortable: `el` must be an HTMLElement, not ".concat({}.toString.call(e));this.el=e,this.options=t=i({},t),e[Te]=this;var n,o,r={group:null,sort:!0,disabled:!1,store:null,handle:null,draggable:/^[uo]l$/i.test(e.nodeName)?">li":">*",swapThreshold:1,invertSwap:!1,invertedSwapThreshold:null,removeCloneOnHide:!0,direction:function(){return ze(e,this.options)},ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",ignore:"a, img",filter:null,preventOnFilter:!0,animation:0,easing:null,setData:function(e,t){e.setData("Text",t.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id",delay:0,delayOnTouchOnly:!1,touchStartThreshold:(Number.parseInt?Number:window).parseInt(window.devicePixelRatio,10)||1,forceFallback:!1,fallbackClass:"sortable-fallback",fallbackOnBody:!1,fallbackTolerance:0,fallbackOffset:{x:0,y:0},supportPointer:!1!==M.supportPointer&&"PointerEvent"in window&&!we,emptyInsertThreshold:5};for(n in $e.initializePlugins(this,e,r),r)n in t||(t[n]=r[n]);for(o in Ke(t),this)"_"===o.charAt(0)&&"function"==typeof this[o]&&(this[o]=this[o].bind(this));this.nativeDraggable=!t.forceFallback&&Ge,this.nativeDraggable&&(this.options.touchStartThreshold=1),t.supportPointer?l(e,"pointerdown",this._onTapStart):(l(e,"mousedown",this._onTapStart),l(e,"touchstart",this._onTapStart)),this.nativeDraggable&&(l(e,"dragover",this),l(e,"dragenter",this)),Ne.push(this.el),t.store&&t.store.get&&this.sort(t.store.get(this)||[]),i(this,R())}function D(e,t,n,i,o,r,a,s){var l,c,d=e[Te],u=d.options.onMove;return!window.CustomEvent||Ce||xe?(l=document.createEvent("Event")).initEvent("move",!0,!0):l=new CustomEvent("move",{bubbles:!0,cancelable:!0}),l.to=t,l.from=e,l.dragged=n,l.draggedRect=i,l.related=o||t,l.relatedRect=r||y(t),l.willInsertAfter=s,l.originalEvent=a,e.dispatchEvent(l),c=u?u.call(d,l,a):c}function B(e){e.draggable=!1}function F(){Fe=!1}function P(e){return setTimeout(e,0)}function H(e){return clearTimeout(e)}function W(){it.forEach(function(e){clearInterval(e.pid)}),it=[]}function V(){clearInterval(tt)}function G(){}function Z(){}function z(e,t){ht.forEach(function(n,i){i=t.children[n.sortableIndex+(e?Number(i):0)],i?t.insertBefore(n,i):t.appendChild(n)})}function K(){ut.forEach(function(e){e!==lt&&e.parentNode&&e.parentNode.removeChild(e)})}var X,Y,U,J,q,Q,ee,te,ne,ie,oe,re,ae,se,le,ce,de,ue,he,pe,fe,ge,me,ye,ve,be,Ce=s(/(?:Trident.*rv[ :]?11\.|msie|iemobile|Windows Phone)/i),xe=s(/Edge/i),Ie=s(/firefox/i),we=s(/safari/i)&&!s(/chrome/i)&&!s(/android/i),Ae=s(/iP(ad|od|hone)/i),ke=s(/chrome/i)&&s(/android/i),_e={capture:!1,passive:!1},Se=/\s+/g,Te="Sortable"+(new Date).getTime(),Ee=[],Re={ -initializeByDefault:!0},$e={mount:function(e){for(var t in Re)!Re.hasOwnProperty(t)||t in e||(e[t]=Re[t]);Ee.forEach(function(t){if(t.pluginName===e.pluginName)throw"Sortable: Cannot mount plugin ".concat(e.pluginName," more than once")}),Ee.push(e)},pluginEvent:function(e,n,i){var o=this;this.eventCanceled=!1,i.cancel=function(){o.eventCanceled=!0};var r=e+"Global";Ee.forEach(function(o){n[o.pluginName]&&(n[o.pluginName][r]&&n[o.pluginName][r](t({sortable:n},i)),n.options[o.pluginName]&&n[o.pluginName][e]&&n[o.pluginName][e](t({sortable:n},i)))})},initializePlugins:function(e,t,n,o){for(var r in Ee.forEach(function(o){var r=o.pluginName;(e.options[r]||o.initializeByDefault)&&((o=new o(e,t,e.options)).sortable=e,o.options=e.options,e[r]=o,i(n,o.defaults))}),e.options){var a;e.options.hasOwnProperty(r)&&void 0!==(a=this.modifyOption(e,r,e.options[r]))&&(e.options[r]=a)}},getEventProperties:function(e,t){var n={};return Ee.forEach(function(o){"function"==typeof o.eventProperties&&i(n,o.eventProperties.call(t[o.pluginName],e))}),n},modifyOption:function(e,t,n){var i;return Ee.forEach(function(o){e[o.pluginName]&&o.optionListeners&&"function"==typeof o.optionListeners[t]&&(i=o.optionListeners[t].call(e[o.pluginName],n))}),i}},Le=["evt"],je=!1,Oe=!1,Ne=[],Me=!1,De=!1,Be=[],Fe=!1,Pe=[],He="undefined"!=typeof document,We=Ae,Ve=xe||Ce?"cssFloat":"float",Ge=He&&!ke&&!Ae&&"draggable"in document.createElement("div"),Ze=function(){if(He){if(Ce)return!1;var e=document.createElement("x");return e.style.cssText="pointer-events:auto","auto"===e.style.pointerEvents}}(),ze=function(e,t){var n=p(e),i=parseInt(n.width)-parseInt(n.paddingLeft)-parseInt(n.paddingRight)-parseInt(n.borderLeftWidth)-parseInt(n.borderRightWidth),o=b(e,0,t),r=b(e,1,t),a=o&&p(o),s=r&&p(r),l=a&&parseInt(a.marginLeft)+parseInt(a.marginRight)+y(o).width,e=s&&parseInt(s.marginLeft)+parseInt(s.marginRight)+y(r).width;return"flex"===n.display?"column"===n.flexDirection||"column-reverse"===n.flexDirection?"vertical":"horizontal":"grid"===n.display?n.gridTemplateColumns.split(" ").length<=1?"vertical":"horizontal":o&&a.float&&"none"!==a.float?(t="left"===a.float?"left":"right",!r||"both"!==s.clear&&s.clear!==t?"horizontal":"vertical"):o&&("block"===a.display||"flex"===a.display||"table"===a.display||"grid"===a.display||i<=l&&"none"===n[Ve]||r&&"none"===n[Ve]&&i=Math.floor(this.options.touchStartThreshold/(this.nativeDraggable&&window.devicePixelRatio||1))&&this._disableDelayedDrag()},_disableDelayedDrag:function(){Y&&B(Y),clearTimeout(this._dragStartTimer),this._disableDelayedDragEvents()},_disableDelayedDragEvents:function(){var e=this.el.ownerDocument;c(e,"mouseup",this._disableDelayedDrag),c(e,"touchend",this._disableDelayedDrag),c(e,"touchcancel",this._disableDelayedDrag),c(e,"mousemove",this._delayedDragTouchMoveHandler),c(e,"touchmove",this._delayedDragTouchMoveHandler),c(e,"pointermove",this._delayedDragTouchMoveHandler)},_triggerDragStart:function(e,t){t=t||"touch"==e.pointerType&&e,!this.nativeDraggable||t?this.options.supportPointer?l(document,"pointermove",this._onTouchMove):l(document,t?"touchmove":"mousemove",this._onTouchMove):(l(Y,"dragend",this),l(q,"dragstart",this._onDragStart));try{document.selection?P(function(){document.selection.empty()}):window.getSelection().removeAllRanges()}catch(e){}},_dragStarted:function(e,t){var n;je=!1,q&&Y?(L("dragStarted",this,{evt:t}),this.nativeDraggable&&l(document,"dragover",N),n=this.options,e||h(Y,n.dragClass,!1),h(Y,n.ghostClass,!0),M.active=this,e&&this._appendGhost(),j({sortable:this,name:"start",originalEvent:t})):this._nulling()},_emulateDragOver:function(){if(de){this._lastX=de.clientX,this._lastY=de.clientY,Xe();for(var e=document.elementFromPoint(de.clientX,de.clientY),t=e;e&&e.shadowRoot&&(e=e.shadowRoot.elementFromPoint(de.clientX,de.clientY))!==t;)t=e;if(Y.parentNode[Te]._isOutsideThisEl(e),t)do{if(t[Te]&&t[Te]._onDragOver({clientX:de.clientX,clientY:de.clientY,target:e,rootEl:t})&&!this.options.dragoverBubble)break}while(t=(e=t).parentNode);Ye()}},_onTouchMove:function(e){if(ce){var t=this.options,n=t.fallbackTolerance,i=t.fallbackOffset,o=e.touches?e.touches[0]:e,r=J&&f(J,!0),a=J&&r&&r.a,s=J&&r&&r.d,t=We&&be&&I(be),a=(o.clientX-ce.clientX+i.x)/(a||1)+(t?t[0]-Be[0]:0)/(a||1),s=(o.clientY-ce.clientY+i.y)/(s||1)+(t?t[1]-Be[1]:0)/(s||1);if(!M.active&&!je){if(n&&Math.max(Math.abs(o.clientX-this._lastX),Math.abs(o.clientY-this._lastY))n.right+10||e.clientX<=n.right&&e.clientY>n.bottom&&e.clientX>=n.left:e.clientX>n.right&&e.clientY>n.top||e.clientX<=n.right&&e.clientY>n.bottom+10}(e,l,this)&&!E.animated){if(E===Y)return o(!1);if((f=E&&d===e.target?E:f)&&(P=y(f)),!1!==D(q,d,Y,a,f,P,e,!!f))return i(),E&&E.nextSibling?d.insertBefore(Y,E.nextSibling):d.appendChild(Y),U=d,r(),o(!0)}else if(E&&function(e,t,n){return n=y(b(n.el,0,n.options,!0)),t?e.clientX=t?e:""+Array(t+1-i.length).join(n)+e},g={s:f,z:function(e){var t=-e.utcOffset(),n=Math.abs(t),i=Math.floor(n/60),o=n%60;return(t<=0?"+":"-")+f(i,2,"0")+":"+f(o,2,"0")},m:function e(t,n){if(t.date()1)return e(a[0])}else{var s=t.name;y[s]=t,o=s}return!i&&o&&(m=o),o||!i&&m},C=function(e,t){if(v(e))return e.clone();var n="object"==typeof t?t:{};return n.date=e,n.args=arguments,new I(n)},x=g;x.l=b,x.i=v,x.w=function(e,t){return C(e,{locale:t.$L,utc:t.$u,x:t.$x,$offset:t.$offset})};var I=function(){function p(e){this.$L=b(e.locale,null,!0),this.parse(e)}var f=p.prototype;return f.parse=function(e){this.$d=function(e){var t=e.date,n=e.utc;if(null===t)return new Date(NaN);if(x.u(t))return new Date;if(t instanceof Date)return new Date(t);if("string"==typeof t&&!/Z$/i.test(t)){var i=t.match(u);if(i){var o=i[2]-1||0,r=(i[7]||"0").substring(0,3);return n?new Date(Date.UTC(i[1],o,i[3]||1,i[4]||0,i[5]||0,i[6]||0,r)):new Date(i[1],o,i[3]||1,i[4]||0,i[5]||0,i[6]||0,r)}}return new Date(t)}(e),this.$x=e.x||{},this.init()},f.init=function(){var e=this.$d;this.$y=e.getFullYear(),this.$M=e.getMonth(),this.$D=e.getDate(),this.$W=e.getDay(),this.$H=e.getHours(),this.$m=e.getMinutes(),this.$s=e.getSeconds(),this.$ms=e.getMilliseconds()},f.$utils=function(){return x},f.isValid=function(){return!(this.$d.toString()===d)},f.isSame=function(e,t){var n=C(e);return this.startOf(t)<=n&&n<=this.endOf(t)},f.isAfter=function(e,t){return C(e)>16)+(t>>16)+(n>>16)<<16|65535&n}function n(e,t){return e<>>32-t}function i(e,i,o,r,a,s){return t(n(t(t(i,e),t(r,s)),a),o)}function o(e,t,n,o,r,a,s){return i(t&n|~t&o,e,t,r,a,s)}function r(e,t,n,o,r,a,s){return i(t&o|n&~o,e,t,r,a,s)}function a(e,t,n,o,r,a,s){return i(t^n^o,e,t,r,a,s)}function s(e,t,n,o,r,a,s){return i(n^(t|~o),e,t,r,a,s)}function l(e,n){e[n>>5]|=128<>>9<<4)]=n;var i,l,c,d,u,h=1732584193,p=-271733879,f=-1732584194,g=271733878;for(i=0;i>5]>>>t%32&255);return n}function d(e){var t,n=[];for(n[(e.length>>2)-1]=void 0,t=0;t>5]|=(255&e.charCodeAt(t/8))<16&&(o=l(o,8*e.length)),n=0;n<16;n+=1)r[n]=909522486^o[n],a[n]=1549556828^o[n];return i=l(r.concat(d(t)),512+8*t.length),c(l(a.concat(i),640))}function p(e){var t,n,i="0123456789abcdef",o="";for(n=0;n>>4&15)+i.charAt(15&t);return o}function f(e){return unescape(encodeURIComponent(e))}function g(e){return u(f(e))}function m(e){return p(g(e))}function y(e,t){return h(f(e),f(t))}function v(e,t){return p(y(e,t))}function b(e,t,n){return t?n?y(t,e):v(t,e):n?g(e):m(e)}"function"==typeof define&&define.amd?define("md5",[],function(){return b}):e.md5=b}(this),define("fun",["lang","toastr","dayjs"],function(Lang,Toastr,Dayjs){var $=layui.jquery,layer=layui.layer,element=layui.element;layer=layer||parent.layer,layui.layer.config({skin:"fun-layer-class"}),Toastr=parent.Toastr||Toastr;var Fun={url:function(e){e=void 0==e?location.href:e;var t=window.location.host;if(e&&-1!==e.indexOf(t))return e;var n=Config.publicAjaxUrl;tempurl=e.split("?");var i=tempurl[1]?"?"+tempurl[1]:"",o=tempurl[0];if(-1!==$.inArray(o,n))return Config.module+e;o=0==o.indexOf("/")?o.replace("/",""):o,o=Fun.common.parseNodeStr(o);var r=o.split("/").length-1;return e=0==r?"/"+Config.appname+"/"+Config.controllername+"/"+o+i:1==r?"/"+Config.appname+"/"+o+i:"/"+o+i},replaceurl:function(e,t){return id=void 0!==t.primaryKeyValue?t.primaryKeyValue:0,e&&(e=-1!==e.indexOf("{ids}")?e.replace("{ids}",id):e),e},checkAuth:function(e,t){return!0===Config.superAdmin||(e.indexOf("?")>=0&&(e=e.replace(/([?#])[^'"]*/,"")),1===$(t).data("node-"+e.toLowerCase()))},param:function(e,t){return void 0!==e?e:t},refreshmenu:function(){top.window.$("#layui-side-left-menu").trigger("refresh")},ajax:function(option,success,error,ex){return option.method=option.method||"post",option.tips=option.tips||"",option.url=option.url||"",option.data=option.data||{},option.statusName=option.statusName||"code",option.statusCode=option.statusCode||1,success=success||function(e){var t=void 0===e.msg&&void 0===e.message?__("Return data is not right"):e.msg?e.msg:e.message;return Fun.toastr.success(t),Fun.toastr.close(),!1},error=error||function(e){var t=void 0===e.msg&&void 0===e.message?__("Return data is not right"):e.msg?e.msg:e.message;return Fun.toastr.error(t),!1},ex=ex||function(e){},""===option.url?(Fun.toastr.error(__("Request url can not empty")),!1):(option.async=!1!==option.async||option.async,$.ajax({url:option.url,type:option.method,contentType:"application/x-www-form-urlencoded; charset=UTF-8",dataType:"json",data:option.data,async:option.async,timeout:0,headers:{"X-CSRF-TOKEN":$('meta[name="csrf-token"]').prop("content")},beforeSend:function(e,t){t.url=Fun.url(t.url)},success:function(res){return eval("res."+option.statusName)>=option.statusCode?success(res):error(res)},error:function(e){var t=void 0!==e.responseJSON?__(e.responseJSON.message):__(",Try again later!");return Fun.toastr.error("Status:"+e.status+"\n"+t,function(){var t=e.getResponseHeader("__token__");t&&$("input[name='__token__']").val(t),ex(this)}),!1},complete:function(e){var t=e.getResponseHeader("__token__");t&&$("input[name='__token__']").val(t)}}),!0)},lang:function(){var e=arguments,t=arguments[0];if(null!==t&&""!==t&&(t=t.toString(),t=t.toLowerCase()),t=void 0!==Lang&&void 0!==Lang[t]?Lang[t]:e[0],"object"==typeof e[1])for(i=0;i0?$(".layui-layer").each(function(){t=Math.max(t,parseInt($(this).attr("times")))}):parent.$(".layui-layer").length&&parent.$(".layui-layer").each(function(){t=Math.max(t,parseInt($(this).attr("times")))}),t)var e=top.layui.layer.confirm(__("are you sure you want to close this window"),{btn:[__("confirm"),__("cancel")]},function(){layui.layer.close(t),top.layui.layer.close(e),parent.layui.layer.close(t)},function(){})}),$("#lock-screen").length>0&&!Fun.api.getStorage("BackendLock")&&$("#lock-screen").remove()}})},bindEvent:function(e,t,n,i){},common:{parseNodeStr:function(e){if(e&&-1===e.indexOf("/")&&(e=Config.controllername+"/"+e),void 0!==e){var t=e.split("/");return layui.each(t,function(e,n){0===e&&(n=n.split("."),layui.each(n,function(e,t){t=Fun.common.lower(Fun.common.snake(t)),n[e]=t.slice(0,1).toLowerCase()+t.slice(1)}),n=n.join("."),t[e]=Fun.common.camel(n))}),e=t.join("/")}return""},camel:function(e){return e.replace(/_(\w)/g,function(e,t){return t.toUpperCase()})},snake:function(e){return e.replace(/([A-Z])/g,"_$1").toLowerCase()},lower:function(e){return e.toLowerCase()},arrTostr:function(e){var t="";for(i=0;i=0?url=url+"&"+ele.primaryKey+"="+d[ele.primaryKey]:url=url+"?"+ele.primaryKey+"="+d[ele.primaryKey]),Fun.checkAuth(node,ele.elem||tableOption.elem)&&(dropdowndata[e]=extend[e],dropdowndata[e].rowindex=e,dropdowndata[e].buttonsindex=data.buttonsindex,dropdowndata[e].url=url,dropdowndata[e].class=t.class||"",dropdowndata[e].id=t.id||t.event,dropdowndata[e].callback=t.callback||"",dropdowndata[e].extend=t.extend||"",dropdowndata[e].type=t.type||"normal",dropdowndata[e].target=t.target||"_self",dropdowndata[e].child=t.child||[],dropdowndata[e].textTitle=t.title,dropdowndata[e].icon=t.icon||"",dropdowndata[e].field=t.field||"",dropdowndata[e].value=t.value||"",icon=extend[e].icon?'':"",dropdowndata[e].templet=t.templet||""+icon+" {{d.title}} ",dropdowndata[e].title=t.title)});var inst=layui.dropdown.render({elem:othis,show:!0,data:dropdowndata,click:function(row,_t){attrEvent=row.event,data.title=row.textTitle,data.rowindex=row.rowindex,buttons=rowData?rowData.data.buttons:tableOption.buttons,buttons=Fun.api.getButtons(buttons,data.buttonsindex,data.rowindex),callback=buttons.extend[data.rowindex].callback,require(["table"],function(Table){Table.events.hasOwnProperty(attrEvent)?Table.events[attrEvent].call(this,_t.find("a"),data,rowData,tableOption):data.callback?eval(data.callback)(_t,data,rowData,tableOption):"function"==typeof callback?callback(_t,data,rowData,tableOption):callback&&"string"==typeof callback?eval(callback)(_t,data,rowData,tableOption):eval(data.event)(_t,data,rowData,tableOption)})},style:"box-shadow: 1px 1px 10px rgb(0 0 0 / 12%);"});return inst.reload(),!1}}},api:{removeEmptyData:function(e){return $.each(e,function(t,n){null===n||void 0===n||""===n?delete e[t]:"object"==typeof n&&Fun.api.removeEmptyData(n)}),e},mergeFunc:function(e,t){return function(){e.apply(this,arguments),t.apply(this,arguments)}},setStorage:function(e,t){null!=t&&"undefined"!==t?layui.data(e,{key:e,value:t}):layui.data(e,{key:e,remove:!0})},getStorage:function(e){var t=layui.data(e);return!!t&&t[e]},setFrameTheme:function(e){var t=Fun.api.getStorage("funColorId");if(t=t||0,theme="theme"+t,Fun.api.getStorage("setFrameTheme")){for(var n=$("#layui-tab .layui-tab-item").find("iframe"),i=0;i768)},checkLayerIframe:function(){return!!parent.$(".layui-layer").length},open:function(options){var title=options.title,url=options.url,width=options.width,height=options.height,success=options.success,cancel=options.cancel,end=options.end,yes=options.yes,type=options.type,autoheight=options.autoheight;type=void 0===type||2===type?2:1;var isResize=void 0===options.isResize,isFull=!!options.full;url=2===type?Fun.url(url):url,isResize=!1===isResize||isResize,width=width||Config.site.site_layer_width,height=height||Config.site.site_layer_height,width=width||"50%",height=height||"100%",width=/%|px/.test(width)?width:$(window).width()+20>=width?width+"px":"95%",height=/%|px/.test(height)?height:$(window).height()+110>=height?height+"px":"100%",autoheight=!!autoheight,offset=options.offset||Config.site.site_layer_offset,anim=Config.site.site_layer_anim?Config.site.site_layer_anim:options.anim,offset=offset||"r",anim=anim||"slideLeft",isFull&&(width="100%",height="100%");var btns=[];void 0==options.btn?(btns=["submit","close"],options.btn_lang=[__("submit"),__("close")]):"false"===options.btn||!1===options.btn||""===options.btn?options.btn_lang=!1:(btnsdata=options.btn,btnsdata=layui.isArray(btnsdata)?btnsdata:btnsdata.split(","),options.btn_lang=[],layui.each(btnsdata,function(e,t){options.btn_lang[e]=__(t),btns.push(t)})),void 0===options.btnAlign&&(options.btnAlign="c"),options.btn_lang===[]&&(options.btn_lang=!1);var parentiframe=Fun.api.checkLayerIframe();if(opt=$.extend(options||{},{title:title,type:type,area:[width,height],content:url,shadeClose:!0,offset:offset,anim:anim,shade:.1,isOutAnim:!0,zIndex:parent.layui.layer.zIndex,maxmin:!0,moveOut:!0,resize:isResize,scrollbar:!0,btnAlign:options.btnAlign,btn:options.btn_lang,success:void 0===success?function(e,t){var n=this;try{$(e).data("callback",n.callback?n.callback:""),parent.layui.layer.setTop(e),autoheight&&parent.layui.layer.iframeAuto(t),e.addClass("layui-form"),e.find(".layui-layer-btn.layui-layer-btn-c").css("background","#f3f6f6"),body=e.find("iframe").contents().find("body"),Fun.api.setFrameTheme(body)}catch(e){console.log(e)}}:success,yes:void 0===yes?function(e,t){try{if(1==btns.length&&"close"==btns[0])return layui.ayer.close(e),!1;$(document).ready(function(){var t=layui.layer.getChildFrame("body",e);parentiframe&&(t=parent.layui.layer.getChildFrame("body",e)),t.find('button[type="'+btns[0]+'"]').trigger("click"),t.find(".layui-hide").hide()})}catch(t){layui.layer.close(e)}return!1}:yes,cancel:void 0===cancel?function(e,t){layui.layer.close(layer.index)}:cancel,end:void 0===end?function(e,t){layui.layer.close(layer.index)}:end}),btns.length>1)for(i=1;i0){var l=[],c=File.prototype.slice||File.prototype.mozSlice||File.prototype.webkitSlice,d=1024*t.init.upload_chunksize*1024,u=1024*t.init.upload_size*1024;fileImageType={audio:"mp3|wma|wav",image:"jpg|jpeg|png|gif|svg|bmp|webp",mp3:"mp3|wma|wav",pdf:"pdf",pptx:"ppt|pptx|doc|docx",txt:"txt",video:"mp4|rmvb|avi|ts",word:"word|doc|docx",xlsx:"xls|xlsx",zip:"rar|tar|zip|7z|gz",office:"ppt|pptx|doc|docx|word|doc|docx|xls|xlsx",file:"*"},layui.each(s,function(e,s){var h=$(this),p=h.data();"object"==typeof p.value&&(p=p.value);var f=p.num,g=h.prop("id")||p.id,m=p.mime,y=p.acceptmime||"",v=p.accept,b=p.path||"upload",C=p.size,x=p.save||0,I=p.group||"",w=p.multiple,A=p.exts,k=p.chunk,f=f||1;C=C||t.init.upload_size,A=A||t.init.upload_exts,A=A.indexOf(",")?A.replace(/,/g,"|"):A,w=w||!1,f=w&&1==f?100:f,v=v||m||"*",v="*"===v?"file":v;var _,S=$(this).parents(".layui-upload"),T=S.find('input[type="text"]'),E=[],R=[];l[e]=$.extend({elem:this,accept:v,size:1024*C,number:f,multiple:w,auto:!1,url:Fun.url(p.url?p.url:t.init.requests.upload_url)+"?path="+b+"&save="+x+"&group_id="+I,before:function(e){void 0==k||0==k||0==k?_=Fun.toastr.loading(__("uploading"),setTimeout(function(){Fun.toastr.close()},1200)):this.data&&(void 0===this.data.chunkId?(window[this.data.chunkId]=layui.layer.open({type:1,title:!1,skin:"chunkProgress",closeBtn:0,resize:!1,shade:.1,area:["420px","20px"],content:['
              ','
              ',"
              "].join(""),success:function(e,t){layui.layer.setTop(e)}}),layui.element.render()):layui.element.progress("uploadProgress",Math.ceil(100*(1+this.data.chunkIndex)/this.data.chunkCount)+"%"))},progress:void 0===a?function(e,t){}:a,choose:void 0===r?function(t){var n=this,i=this.files=t.pushFile();t.preview(function(o,r,a){if(r.size>u)return delete i[o],Fun.toastr.error(__("文件大小超过限制,最大不超过"+u/1024+"KB")),!1;if(r.size<=d||void 0==k||0==k||0==k)console.log(1),t.upload(o,r),delete i[o];else if(1==k||1==k){var s=r.lastModified+"-"+r.size,l=Math.ceil(r.size/d),h=/\.([0-9A-z]+)$/.exec(r.name)[1],p=[];for(e=0;e0)if(void 0===e.data.chunkId||e.data.url){void 0!==e.data.chunkId&&(layui.layer.close(window[e.data.chunkId]),delete R[e.data.chunkId],delete E[e.data.chunkId],e.url=e.data.url);var i=e.url.lastIndexOf(".");ext=e.url.substring(i+1,e.url.length).toLowerCase();var o="";for(var r in fileImageType){if(-1!==fileImageType[r].indexOf(ext)){o=r;break}if("*"==fileImageType[r]){o=r;break}}console.log(o),html="image"==o?'
            • \n':'
            • \n';var a=T.val();if(1==f)T.val(e.url),S.find(".layui-upload-list").html(html);else if("*"==f)S.find(".layui-upload-list").append(html),val_temp=a?a+","+e.url:e.url,T.val(val_temp);else{if(S.find("li").length>=f)return Fun.toastr.error(__("File nums is limited")),!1;S.find(".layui-upload-list").append(html),val_temp=a?a+","+e.url:e.url,T.val(val_temp)}Fun.toastr.success(__("Upload Success"),setTimeout(function(){Fun.toastr.close()},1500))}else{var s=e.data.chunkIndex,l=e.data.chunkId,u=e.data.chunkCount,p=R[l][s];if(s+10){var a=[];layui.each(r,function(e){var r=$(this),s=r.parents(".layui-upload"),l=r.prop("id")||r.data("id"),c=r.data();"object"==typeof c.value&&(c=c.value);var d=c.width||300,u=c.height||300,h=c.mark||1,p=c.area,f=c.path||"upload";a[e]=$.extend({elem:$(this),saveW:d,saveH:u,mark:h,area:p,url:Fun.url(c.url?c.url:t.init.requests.upload_url)+"?path="+f,done:void 0===i?function(e){if(e.code>0){Fun.toastr.success(e.msg),s.find('input[type="text"]').val(e.url);var t='
            • \n';s.find(".layui-upload-list").html(t)}else e.code<=0&&Fun.toastr.error(e.msg)}:i,error:void 0===o?function(e){}:o},void 0===n?{}:n),window["cropper-"+l]=layui.croppers.render(a[e])})}},bindEvent:function(){t.events.uploads(),t.events.cropper()}}});return t}),define("form",["upload"],function(Upload){var $=layui.$,Form={init:{},events:{events:function(){list=$("*[lay-event]"),list.length>0&&layui.each(list,function(){$(this).click(function(e){return"open"===$(this).attr("lay-event")?(Fun.events.open($(this)),!0):"request"===$(this).attr("lay-event")?(Fun.events.request($(this)),!0):"iframe"===$(this).attr("lay-event")?(Fun.events.iframe($(this)),!0):"dropdown"===$(this).attr("lay-event")?(Fun.events.dropdown($(this)),!0):void 0})})},autocomplete:function(e){Form.api.autocomplete(e)},selects:function(e){Form.api.selects(e)},selectcx:function(e){Form.api.selectcx(e)},selectpage:function(e){Form.api.selectpage(e)}, -xmselect:function(e){Form.api.xmselect(e)},editor:function(e){Form.api.editor(e)},tags:function(e){Form.api.tags(e)},icon:function(e){Form.api.icon(e)},color:function(e){Form.api.color(e)},city:function(e){Form.api.city(e)},timepicker:function(e){Form.api.timepicker(e)},datepicker:function(e){Form.api.datepicker(e)},date:function(e){Form.api.date(e)},rate:function(e){Form.api.rate(e)},slider:function(e){Form.api.slider(e)},formarray:function(e){Form.api.formarray(e)},uploads:function(e){Upload.api.uploads()},cropper:function(e){Upload.api.cropper()},choosefiles:function(e){Form.api.chooseFiles(e)},selectfiles:function(e){Form.api.selectFiles(e)},json:function(e){Form.api.json(e)},transfer:function(e){Form.api.transfer(e)},verifys:function(e){Form.api.verifys(e)},required:function(e){Form.api.required(e)},submit:function(e,t,n,i){var o=$("[lay-submit]");o.length>0&&layui.each(o,function(o){var r=$(this).attr("lay-filter"),a=$(this).data("type"),s=$(this).data("refresh"),l=$(this).data("request")||$(this).data("url")||$('form[lay-filter="'+r+'"]').attr("action");if("tableSearch"===a)return!1;void 0===s&&(s=!0),"false"===s&&(s=!1),""===s&&(s=!1),void 0!==r&&""!==r||(r="form-"+(o+1),$(this).attr("lay-filter",r)),void 0!==l&&""!==l&&null!=l||(l=location.href),layui.form.on("submit("+r+")",function(o){if($("select[multiple]").length>0){var r=$("select[multiple]");layui.each(r,function(){var e=$(this).attr("name"),t=[];$(this).children("option:selected").each(function(){t.push($(this).val())}),o.field[e]=t.join(",")})}var a=o.field;return"function"==typeof e?e(l,a):"function"==typeof i?i(l,a):Form.api.formSubmit(l,a,t,n,s),!1})})},photos:function(e){Fun.events.photos(e)},filedelete:function(e){Form.api.filedelete(e)},bindevent:function(e){e.on("click","[lay-event]",function(){var e=$(this),t=e.attr("lay-event");Form.events.hasOwnProperty(t)&&Form.events[t]&&Form.events[t].call(this,e)}),require(["table"],function(e){$("body").on("click","[lay-event]",function(){if($("table").length>0){var t=$(this),n=t.attr("lay-event");e.events.hasOwnProperty(n)&&e.events[n]&&e.events[n].call(this,t)}})})}},api:{closeOpen:function(e){e=e||{},e.refreshTable=e.refreshTable||!1,e.refreshFrame=e.refreshFrame||!1;var t=parent.layui.layer.getFrameIndex(window.name);return t&&parent.layui.layer.close(t),!0===e.refreshTable&&require(["table"],function(t){if(e.refreshTable=e.tableid||t.init.tableId,!(self!==top&&parent.$("#"+e.refreshTable).length>0))return setTimeout(function(){location.reload()},2e3),!1;t.api.reload(e.refreshTable)}),!!e.refreshFrame&&(setTimeout(function(){location.reload()},2e3),!1)},formSubmit:function(e,t,n,i,o){return n=n||function(e){return e.msg=e.msg||"success",Fun.toastr.success(e.msg),Form.api.closeOpen({refreshTable:o,refreshFrame:o}),!1},i=i||function(e){return e.msg=e.msg||"error",Fun.toastr.error(e.msg,function(){}),!1},Fun.ajax({url:e,data:t,complete:function(e){var t=e.getResponseHeader("__token__");t&&$("input[name='__token__']").val(t)}},n,i),!1},required:function(e){var t=$("[lay-verify]");t.length>0&&(layui.each(t,function(){var e=$(this).attr("lay-verify");if(e&&e.indexOf("required")>=0){var t=$(this).parent().prev();t.is("label")&&!t.hasClass("required")&&t.addClass("required"),void 0===$(this).attr("lay-reqtext")&&void 0!==$(this).attr("placeholder")&&$(this).attr("lay-reqtext",$(this).attr("placeholder")),void 0===$(this).attr("placeholder")&&void 0!==$(this).attr("lay-reqtext")&&$(this).attr("placeholder",$(this).attr("layreqtext"))}}),$("div[lay-verify]").removeAttr("lay-verify"))},verifys:function(e){layui.form.verify({user:function(e){return new RegExp("^[a-zA-Z0-9_一-龥\\s·]+$").test(e)?/(^\_)|(\__)|(\_+$)/.test(e)?"用户名首尾不能出现下划线'_'":/^\d+\d+\d$/.test(e)?"用户名不能全为数字":void 0:"用户名不能有特殊字符"},pass:[/^[\S]{6,18}$/,"密码必须6到18位,且不能出现空格"],zipcode:[/^\d{6}$/,"请检查邮政编码格式"],chinese:[/^[\u0391-\uFFE5]+$/,"请填写中文字符"],money:[/(^[1-9]([0-9]+)?(\.[0-9]{1,2})?$)|(^(0){1}$)|(^[0-9]\.[0-9]([0-9])?$)/,"请输入正确的金额,且最多两位小数!"],letters:[/^[a-z]+$/i,"请填写字母"],digits:[/^\d+$/,"请填入数字"],qq:[/^[1-9]\d{4,}$/,"请填写有效的QQ号"]})},initForm:function(e){e?(Fun.api.removeEmptyData(e),layui.form.val("form",e)):window.FormArray&&(window.FormArray=Fun.api.removeEmptyData(window.FormArray),layui.form.val("form",window.FormArray)),layui.form.render()},filedelete:function(e){var t,n=e.data("fileurl"),i=Fun.toastr.confirm(__("Are you sure?"),function(){t=e.parents(".layui-upload-list").parents(".layui-upload");var o,r=t.find('input[type="text"]'),a=r.val();0===e.parents("li").index()?(o=a.replace(n,""),r.val(o.replace(/^,|,$/g,""))):(o=a.replace(","+n,""),r.val(o.replace(/^,|,$/g,""))),e.parents("li").remove(),Fun.toastr.close(i)});return!1},autocomplete:function(e){var t=void 0!==e?e.find("*[lay-filter='autoComplete']"):$("*[lay-filter='autoComplete']");t.length>0&&require(["autoComplete"],function(e){layui.each(t,function(t,n){var i=$(this),o=i.data("data"),r=i.data("src")||i.data("url"),a=i.attr("id")||i.data("id"),s=i.data("keys")||null;s&&(s=[s]),0==o.length&&(o=Fun.api.getData(r,{})),window["autoComplete-"+a]=new e({selector:"#"+a,placeHolder:"Search...",data:{src:o,keys:s,cache:!0},resultsList:{element:function(e,t){if(!t.results.length){var n=document.createElement("div");n.setAttribute("class","autocomplete_no_result"),n.innerHTML='Found No Results for "'+t.query+'"',e.prepend(n)}},noResults:!0},resultItem:{highlight:!0},events:{input:{selection:function(e){const t=e.detail.selection.value;window["autoComplete-"+a].input.value=s?t[s]:t}}}})})})},selects:function(e){var t=void 0!==e?e.find("*[lay-filter='selects']"):$("*[lay-filter='selects']");t.length>0&&require(["selects"],function(e){e=layui.selects||parent.layui.selects,layui.each(t,function(t){var n=$(this),i=n.prop("id"),o=(n.attr("name"),n.data("url")||n.data("request")),r=n.data("data")||[],a=n.data("value")?$(this).data("value"):"",s=n.data("attr"),s=s&&"string"==typeof s?s.split(","):["id","title"];attrs={},layui.isArray(s)&&(attrs.id=s.shift(),attrs.title=s.shift(),attrs.selected=s[2]?s[2]:"selected");var l={elem:this,keywordPlaceholder:"请输入关键词",unfilteredText:"没有匹配的选项",valueSeparator:",",options:[],allowCreate:!0,collapseSelected:!1,url:void 0,parseOptions:void 0};if(attrs&&(l.customName=attrs),r){datas=[];for(k in r)r[k].hasOwnProperty("id")||(datas[t]={id:k,title:r[k]});r=datas.length>0?datas:r,l.options=r}o&&(l.url=Fun.url(o),l.parseOptions=function(e){return e.data}),window["selects-"+i]=e.render(l),window["selects-"+i].val(String(a))})})},selectcx:function(e){var t=void 0!==e?e.find("*[lay-filter='cxselect']"):$("*[lay-filter='cxselect']");t.length>0&&require(["cxSelect"],function(e){layui.each(t,function(e,t){var n=$(this),i=n.data("selects"),o=n.data("url"),r=n.data("attr")||["id","name"];r=layui.isArray(r)?r:r.split(","),$.cxSelect.defaults.jsonValue=r[0]||"id",$.cxSelect.defaults.jsonName=r?r[1]:"name",$.cxSelect.defaults.jsonSpace=n.data("url")?"data":"",n.cxSelect({url:o,selects:i,emptyStyle:"none"})})})},selectpage:function(formObj){var list=void 0!==formObj?formObj.find("*[lay-filter='selectPage']"):$("*[lay-filter='selectPage']");list.length>0&&require(["selectPage"],function(selectPage){selectPage=layui.selectPage||parent.layui.selectPage,layui.each(list,function(i){var _t=$(this);value=_t.val()||_t.data("init");var id=_t.prop("id"),name=_t.attr("name")||"id",verify=_t.data("verify")||_t.attr("verify"),url=_t.data("url")||_t.data("request"),isTree=_t.data("istree"),isHtml=_t.data("ishtml"),data=_t.data("data"),field=_t.data("field")||"title",pageSize=_t.data("pagesize")||12,primaryKey=_t.data("primarkey")||"id",selectOnly=_t.data("selectonly")||!1,pagination=!("false"==_t.data("pagination")||0==_t.data("pagination")),listSize=_t.data("listsize")||"15",multiple=_t.data("multiple")||!1,dropButton=_t.data("dropbutton")||!0,maxSelectLimit=_t.data("maxselectlimit ")||0,searchField=_t.data("searchfield")||field,searchKey=_t.data("searchkey")||primaryKey,orderBy=_t.data("orderby")||!1,method=_t.data("method")||"GET",dbTable=_t.data("dbtable"),selectToCloseList=_t.data("selecttocloselist")||!0,disabled=_t.data("disabled")||!1,andOr=_t.data("andor"),formatItem=_t.data("formatitem")||!1,verify=_t.attr("lay-verify")||"";orderBy="string"==layui.type(orderBy)?[orderBy]:orderBy,eSelect=_t.data("eselect"),!value&&window.FormArray&&window.FormArray[name]&&_t.val(window.FormArray[name]);var options={showField:field,keyField:primaryKey,pageSize:pageSize,selectFields:searchField,searchKey:searchKey,isTree:isTree,isHtml:isHtml,data:data||Fun.url(url),dbTable:dbTable,andOr:andOr,method:method,selectOnly:selectOnly,verify:verify,selectToCloseList:selectToCloseList,pagination:pagination,maxSelectLimit:maxSelectLimit,orderBy:orderBy,listSize:listSize,multiple:multiple,dropButton:dropButton,formatItem:function(res){return formatItem?eval(formatItem):res[this.showField]},eSelect:eSelect?eval(eSelect)(res):function(e){},eAjaxSuccess:function(e){return row=e.data,data={},data.list=void 0!==row.data?row.data:[],data.totalRow=void 0!==row.count?row.count:row.data.length,data}};window["selectpage-"+id]=_t.selectPage(options),disabled&&_t.selectPageDisabled(!0)})})},xmselect:function(formObj){var list=void 0!==formObj?formObj.find("*[lay-filter='xmSelect']"):$("*[lay-filter='xmSelect']")||$("*[lay-filter='xmselect']");list.length>0&&require(["xmSelect"],function(xmSelect){layui.each(list,function(i){var _t=$(this),id=_t.prop("id"),url=_t.data("url")||_t.data("request"),lang=_t.data("lang"),value=_t.data("value")||_t.attr("value"),data=_t.data("data")||[],parentfield=_t.data("parentfield")||"pid",tips=_t.data("tips")||"请选择",searchTips=_t.data("searchtips")||"请选择",empty=_t.data("empty")||"呀,没有数据",height=_t.data("height")||"auto",paging=_t.data("paging"),pageSize=_t.data("pagesize"),remoteMethod=_t.data("remotemethod"),content=_t.data("content")||"",radio=_t.data("radio"),disabled=_t.data("disabled"),autoRow=!1!==_t.data("autorow"),clickClose=_t.data("clickclose"),prop=_t.data("prop")||_t.data("attr"),max=_t.data("max"),create=_t.data("create"),on=$(this).data("on"),repeat=!!_t.data("repeat"),theme=_t.data("theme")||"#4d70ff",name=_t.attr("name")||_t.data("name")||"pid",style=_t.data("style")||{},cascader=!!_t.data("cascader")&&{show:!0,indent:200,strict:!1},layVerify=_t.attr("lay-verify")||_t.data("layverify")||"",layReqText=_t.attr("lay-reqtext")||_t.data("layreqtext")||"";layVerType=_t.attr("lay-vertype")||_t.data("layvertype")||"tips";var size=_t.data("size")||"medium";toolbar=0==_t.data("toolbar")?{show:!1}:{show:!0,list:["ALL","CLEAR","REVERSE"]};var filterable=!(void 0!==$(this).data("filterable")&&!_t.data("filterable")),remoteSearch=!(void 0===$(this).data("remotesearch")||!_t.data("remotesearch")),pageRemote=!!$(this).data("pageremote"),props,propArr,options,tree=_t.data("tree");remoteSearch&&(toolbar.show=!0),filterable=!0,tree="object"==typeof tree?tree:!!tree&&{show:!0,showFolderIcon:!0,showLine:!0,indent:20,expandedKeys:[],strict:!1,simple:!1,clickExpand:!0,clickCheck:!0},"object"!=typeof value&&value&&(value="number"==typeof value?[value]:value.split(",")),props={name:"title",value:"id"},selelectFields={name:"title",value:"id"},prop&&(propArr=prop.split(","),props.name=propArr[0],props.value=propArr[1],selelectFields={name:props.name},selelectFields.value=propArr[1]==props.name?"id":propArr[1]),lang=lang||"zh",paging=void 0===paging||"false"!==paging,pageSize=pageSize||10,radio=!!radio,disabled=!!disabled,max=max||0,clickClose=clickClose||!1,xmSelect=window.xmSelect?window.xmSelect:parent.window.xmSelect,options={el:this,language:lang,data:data,initValue:value,name:name,prop:props,tips:tips,empty:empty,searchTips:searchTips,disabled:disabled,filterable:filterable,remoteSearch:remoteSearch,remoteMethod:function(val,cb,show){if(remoteMethod&&void 0!==remoteMethod)eval(remoteMethod)(val,cb,show);else{var formatFilter={},formatOp={};formatFilter[props.name]=val,formatOp[props.name]="%*%",Fun.ajax({method:"get",url:Fun.url(url||window.location.href),data:{filter:JSON.stringify(formatFilter),op:JSON.stringify(formatOp),selectFields:selelectFields}},function(e){cb(e.data)},function(e){cb([])})}},paging:paging,pageSize:pageSize,autoRow:autoRow,size:size,repeat:repeat,height:height,max:max,pageRemote:pageRemote,toolbar:toolbar,theme:{color:theme},radio:radio,layVerify:layVerify,clickClose:clickClose,maxMethod:function(e){},on:function(data){return!!eval(on)&&eval(on)(data)},create:function(val,arr){return eval(create)?eval(create)(val,arr):{name:val,value:val}}},tree&&(options.tree=tree),cascader&&(options.cascader=cascader),style&&(options.style=style),layReqText&&(options.layReqText=layReqText),layVerType&&(options.layVerType=layVerType),content&&(options.content=content),window["xmselect-"+id]=xmSelect.render(options),""===data.toString()&&url&&(searchData={selectFields:selelectFields,tree:tree.show,parentField:parentfield},Fun.ajax({method:"GET",url:Fun.url(url||window.location.href),data:searchData},function(e){window["xmselect-"+id].update({data:e.data,autoRow:autoRow})},function(e){console.log(e)}))})})},editor:function(e){var t=void 0!==e?e.find("*[lay-filter='editor']"):$("*[lay-filter='editor']");if(t.length>0){const n=window.matchMedia("(prefers-color-scheme: dark)").matches,i=window.matchMedia("(max-width: 1023.5px)").matches;layui.each(t,function(){var e=$(this),t=e.prop("id"),o=e.data(),r=e.prop("name"),a=e.data("path");e.html(window.FormArray[r]);var s=(o.url?o.url:Fun.url(Upload.init.requests.upload_url))+"?editor=tinymce&path="+a;"tinymce"==$(this).data("editor")&&(0==$("body").find('script[src="/static/plugins/tinymce/tinymce.min.js"]').length&&$("body").append($("")); } window['editor-' + id] = tinymce.init({ selector: '#' + id + '[lay-editor]', license_key:'gpl', - language: data.language || 'zh_CN', - plugins: data.plugins ? data.plugins : 'preview importcss searchreplace autolink autosave save directionality code visualblocks visualchars fullscreen image link media codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount help charmap quickbars emoticons', + language: dataOptions.language || 'zh_CN', + plugins: dataOptions.plugins ? dataOptions.plugins : 'preview importcss searchreplace autolink autosave save directionality code visualblocks visualchars fullscreen image link media codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount help charmap quickbars emoticons', editimage_cors_hosts: ['picsum.photos'], menubar: 'file edit view insert format tools table help', - toolbar: data.toolbar ? data.toobar : 'undo redo bold italic underline strikethrough fontfamily fontsize | blocks alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist | forecolor backcolor removeformat | pagebreak | charmap emoticons | fullscreen insertfile image media preview template link | print anchor codesample save ltr rtl ', + toolbar: dataOptions.toolbar ? dataOptions.toolbar : 'undo redo bold italic underline strikethrough fontfamily fontsize | blocks alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist | forecolor backcolor removeformat | pagebreak | charmap emoticons | fullscreen insertfile image media preview template link | print anchor codesample save ltr rtl ', toolbar_sticky: false, toolbar_sticky_offset: isSmallScreen ? 102 : 108, autosave_ask_before_unload: true, @@ -734,12 +855,11 @@ autosave_restore_when_empty: false, autosave_retention: '20m', image_advtab: true, - height: (data.height ? data.height : 650), //编辑器高度 - min_height: (data.maxheight ? data.maxheight : 400), - content_style: "body { font-family:Helvetica,Arial,sans-serif; font-size:16px } img {max-width:100%;}", + height: (dataOptions.height ? dataOptions.height : 650), //编辑器高度 + min_height: (dataOptions.maxheight ? dataOptions.maxheight : 400), + content_style: "body {font-size:16px } img {max-width:100%;}", font_size_formats:'10px 11px 12px 13px 14px 15px 16px 18px 20px 22px 24px 26px 28px 30px 32px 34px 36px', font_size_input_default_unit: "px", - font_formats: '微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;宋体=simsun,serif;仿宋体=FangSong,serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;', link_list: [ {title: 'funadmin', value: 'https://www.tiny.cloud'}, {title: 'my funadmin', value: 'http://www.funadmin.com'}], @@ -807,17 +927,23 @@ }) } }, - tags: function (formObj) { - var list = formObj !== undefined ? formObj.find("*[lay-filter='inputtags']") : $("*[lay-filter='inputtags']"); + tags: function (formObj, elem) { + elem = elem || ''; + if(elem){ + var list = elem; + }else{ + var list = formObj !== undefined ? formObj.find("*[lay-filter='inputtags']") : $("*[lay-filter='inputtags']"); + } if (list.length > 0) { require(['inputTags'], function (inputTags) { layui.each(list, function () { - var _t = $(this), data = [],value = _t.next('input').val(); - if (value) data = value.substring(0, value.length - 1).split(','); + var _t = $(this), + valueData = [],value = _t.next('input').val(); + if (value) valueData = value.substring(0, value.length - 1).split(','); var id = _t.prop('id'); window['tags-' + id] = inputTags.render({ elem: this, - data: data,//初始值 + data: valueData,//初始值 permanentData: [],//不允许删除的值 removeKeyNum: 8,//删除按键编号 默认,BackSpace 键 createKeyNum: 32,//创建按键编号 默认,space 键 @@ -832,36 +958,48 @@ }) } }, - icon: function (formObj) { - var list = formObj !== undefined ? formObj.find("*[lay-filter='iconPicker']") : $("*[lay-filter='iconPicker']"); + icon: function (formObj, elem) { + elem = elem || ''; + if(elem){ + var list = elem; + }else{ + var list = formObj !== undefined ? formObj.find("*[lay-filter='iconPicker']") : $("*[lay-filter='iconPicker']"); + } if (list.length > 0) { require(['iconPicker'], function (iconPicker) { layui.each(list, function () { - var _t = $(this);var url = _t.data('url'); - var data = _t.data();var id = _t.prop('id'); + var _t = $(this); + var dataOptions = Fun.api.getElementData(_t); + var id = _t.prop('id'); window['icon-' + id] = layui.iconPicker.render({ elem: this, type: 'fontClass', - value:_t.prop('value') || _t.data('value'), + value:_t.prop('value') || dataOptions.value, search: true, page: true, limit: 12, click: function (data) { _t.val(data.icon) }, - success: data.done ? eval(data.done(d)) : function (d) { + success: dataOptions.done ? eval(dataOptions.done(data)) : function (d) { } }) }) }) } }, - color: function (formObj) { - var list = formObj !== undefined ? formObj.find("*[lay-filter='colorPicker']") : $("*[lay-filter='colorPicker']"); + color: function (formObj, elem) { + elem = elem || ''; + if(elem){ + var list = elem; + }else{ + var list = formObj !== undefined ? formObj.find("*[lay-filter='colorPicker']") : $("*[lay-filter='colorPicker']"); + } if (list.length > 0) { layui.each(list, function () { - var _t = $(this), name = _t.data('name'), format = _t.data('format') || 'hex', - data = _t.data(); + var _t = $(this); + var dataOptions = Fun.api.getElementData(_t); + var name = dataOptions.name, format = dataOptions.format || 'hex'; var id = _t.prop('id') || _t.data('id'); var color = _t.prev('input').val(); window['color-' + id] = layui.colorpicker.render({ @@ -872,24 +1010,30 @@ format: format, change: function (color) { }, - done: data.done ? eval(data.done(color)) : function (color) { + done: dataOptions.done ? eval(dataOptions.done(color)) : function (color) { _t.prev('input[name="' + name + '"]').val(color) } }) }) } }, - city: function (formObj) { - var list = formObj !== undefined ? formObj.find("*[lay-filter='cityPicker']") : $("*[lay-filter='cityPicker']"); + city: function (formObj, elem) { + elem = elem || ''; + if(elem){ + var list = elem; + }else{ + var list = formObj !== undefined ? formObj.find("*[lay-filter='cityPicker']") : $("*[lay-filter='cityPicker']"); + } if (list.length > 0) { require(['cityPicker'], function (cityPicker) { cityPicker = layui.cityPicker; layui.each(list, function () { var _t = $(this); + var dataOptions = Fun.api.getElementData(_t); var id = _t.prop('id'), name = _t.prop('name'); - var provinceId = _t.data('provinceid'), - cityId = _t.data('cityid'); + var provinceId = dataOptions.provinceid, + cityId = dataOptions.cityid; var province, city, district; if (window.FormArray[name]) { var cityValue = window.FormArray[name]; @@ -897,7 +1041,7 @@ city = cityValue.split('/')[1]; district = cityValue.split('/')[2]; } - var districtId = _t.data('districtid'); + var districtId = dataOptions.districtid; window['citypicker-' + id] = new cityPicker(this, { provincename: provinceId, cityname: cityId, @@ -925,8 +1069,13 @@ }) } }, - timepicker: function (formObj) { - var list = formObj !== undefined ? formObj.find("*[lay-filter='timePicker']") : $("*[lay-filter='timePicker']"); + timepicker: function (formObj, elem) { + elem = elem || ''; + if(elem){ + var list = elem; + }else{ + var list = formObj !== undefined ? formObj.find("*[lay-filter='timePicker']") : $("*[lay-filter='timePicker']"); + } if (list.length > 0) { require(['timePicker'], function (timePicker) { layui.each(list, function () { @@ -943,11 +1092,17 @@ }) } }, - datepicker: function (formObj) { - var list = formObj !== undefined ? formObj.find("*[lay-filter='datePicker']") : $("*[lay-filter='datePicker']"); + datepicker: function (formObj, elem) { + elem = elem || ''; + if(elem){ + var list = elem; + }else{ + var list = formObj !== undefined ? formObj.find("*[lay-filter='datePicker']") : $("*[lay-filter='datePicker']"); + } if (list.length > 0) { layui.each(list, function () { - var id = $(this).prop('id') || $(this).attr('id'); + var _t = $(this); + var id = _t.prop('id') || _t.attr('id'); window['datepicker-' + id] = layui.laydate.render({ elem: this, type: "datetime", @@ -1129,19 +1284,28 @@ }) } }, - date: function (formObj) { - var list = formObj !== undefined ? formObj.find("*[lay-filter='date']") : $("*[lay-filter='date']"); + date: function (formObj, elem) { + elem = elem || ''; + if(elem){ + var list = elem; + }else{ + var list = formObj !== undefined ? formObj.find("*[lay-filter='date']") : $("*[lay-filter='date']"); + } if (list.length > 0) { layui.each(list, function () { - var data = $(this).data(), format = $(this).data('format'), type = $(this).data('type'), - id = $(this).prop('id') || $(this).data('id'), - value = $(this).data('value') || $(this).val(), range = $(this).data('range'); + var _t = $(this); + var dataOptions = Fun.api.getElementData(_t); + var format = dataOptions.format, type = dataOptions.type, + id = _t.prop('id') || _t.data('id'), + value = dataOptions.value || _t.val(), range = dataOptions.range; + type = type || 'datetime'; + theme = dataOptions.theme || '#4d70ff'; var options = { elem: this, - type: data.type || 'datetime', - trigger: 'click', + type: type, + trigger: dataOptions.trigger || 'click', calendar: true, - theme: data.theme || '#4d70ff' + theme: theme }; if (format !== undefined && format !== '' && format != null) { options['format'] = format.replaceAll('Y','y'); @@ -1155,19 +1319,19 @@ if (value) { options['value'] = value; } - if (data.fullPanel != false) { + if (dataOptions.fullPanel != false) { options['fullPanel'] = true;// 2.8+ } - if (data.mark) { + if (dataOptions.mark) { options['mark'] = mark; } - if (data.holidays) { + if (dataOptions.holidays) { options['holidays'] = holidays; } - if (data.min) { + if (dataOptions.min) { options['min'] = min; } - if (data.max) { + if (dataOptions.max) { options['max'] = max; } laydate = layui.laydate ? layui.laydate : parent.layui.laydate; @@ -1175,17 +1339,29 @@ }) } }, - rate: function (formObj) { - var list = formObj !== undefined ? formObj.find("*[lay-filter='rate']") : $("*[lay-filter='rate']"); + rate: function (formObj, elem) { + elem = elem || ''; + if(elem){ + var list = elem; + }else{ + var list = formObj !== undefined ? formObj.find("*[lay-filter='rate']") : $("*[lay-filter='rate']"); + } if (list.length > 0) { layui.each(list, function (i) { - var _t = $(this), id = _t.prop('id'), name = _t.data('name') - options = _t.data('options') || {}; - options.elem = this; - options.value = _t.data('value'); - options.length = options.length ? options.length : 5; - options.theme = options.theme == undefined ? '#1E9FFF' : options.length; - options.readonly = options.readonly || options.disabled ? true : false; + var _t = $(this), id = _t.prop('id'); + var dataOptions = Fun.api.getElementData(_t); + var name = dataOptions.name; + var value = dataOptions.value; + var length = dataOptions.length || 5; + var theme = dataOptions.theme || '#1E9FFF'; + var readonly = dataOptions.readonly || dataOptions.disabled ? true : false; + var options = { + elem: this, + value: value, + length: length, + theme: theme, + readonly: readonly + }; if (_t.parent('div').find('input[name="' + name + '"]').length == 0) { _t.before('input[name="' + name + '"]'); } @@ -1202,17 +1378,39 @@ }) } }, - slider: function (formObj) { - var list = formObj !== undefined ? formObj.find("*[lay-filter='slider']") : $("*[lay-filter='slider']"); + slider: function (formObj, elem) { + elem = elem || ''; + if(elem){ + var list = elem; + }else{ + var list = formObj !== undefined ? formObj.find("*[lay-filter='slider']") : $("*[lay-filter='slider']"); + } if (list.length > 0) { layui.each(list, function (i) { - var _t = $(this), id = _t.prop('id'), name = _t.data('name'); - options = _t.data('options') || {}; - options.elem = this; - options.value = _t.data('value'); - options.type = _t.data('type') || 'default'; - options.step = _t.data('step') || 1; - options.range = _t.data('range') || true; + var _t = $(this), id = _t.prop('id'); + var dataOptions = Fun.api.getElementData(_t); + var name = dataOptions.name; + var value = dataOptions.value; + var type = dataOptions.type || 'default'; + var step = dataOptions.step || 1; + var range = dataOptions.range || true; + var max = dataOptions.max || 100; + var min = dataOptions.min || 0; + var disabled = dataOptions.disabled || dataOptions.readonly ? true : false; + var input = dataOptions.input == undefined || dataOptions.input ? true : false; + var theme = dataOptions.theme || '#1E9FFF'; + var options = { + elem: this, + value: value, + type: type, + step: step, + range: range, + max: max, + min: min, + disabled: disabled, + input: input, + theme: theme + }; options.max = options.max ? options.max : 100; options.min = options.min ? options.min : 0; options.disabled = options.readonly || options.disabled ? true : false; @@ -1234,7 +1432,8 @@ }) } }, - formarray: function (formObj) { + formarray: function (formObj, elem) { + elem = elem || ''; formObj.on("click", ".form-array .del", function () { var tr = $(this).parents('tr'); var lawtable = tr.parents('.layui-table'); @@ -1268,23 +1467,28 @@ /** * 选择文件 */ - chooseFiles: function (formObj) { - var fileChooseList = formObj !== undefined ? formObj.find("*[lay-filter='upload-choose']") : $("*[lay-filter='upload-choose']"); + chooseFiles: function (formObj, elem) { + elem = elem || ''; + if(elem){ + var fileChooseList = elem; + }else{ + var fileChooseList = formObj !== undefined ? formObj.find("*[lay-filter='upload-choose']") : $("*[lay-filter='upload-choose']"); + } if (fileChooseList.length > 0) { require(['tableSelect', 'table'], function (tableSelect, Table) { layui.each(fileChooseList, function (i, v) { - var data = $(this).data(); - if (typeof data.value == 'object') data = data.value; - var uploadType = data.type, uploadNum = data.num, uploadMime = data.mime, - url = data.tableurl, path = data.path; + var _t = $(this); + var dataOptions = Fun.api.getElementData(_t); + var uploadType = dataOptions.type, uploadNum = dataOptions.num, uploadMime = dataOptions.mime, + url = dataOptions.tableurl, path = dataOptions.path; uploadMime = uploadMime || '*'; uploadType = uploadType ? uploadType : 'radio'; uploadNum = uploadType === 'checkbox' ? uploadNum : 1; - var input = $(this).parents('.layui-upload').find('input[type="text"]'); - var uploadList = $(this).parents('.layui-upload').find('.layui-upload-list'); - var id = $(this).attr('id'); + var input = _t.parents('.layui-upload').find('input[type="text"]'); + var uploadList = _t.parents('.layui-upload').find('.layui-upload-list'); + var id = _t.attr('id'); tableSelect = layui.tableSelect || parent.layui.tableSelect; - url = url ? url : Fun.url(Upload.init.requests.attach_url + '?' + + url = url ? url : Fun.url(Upload.init.requests.attach + '?' + '&elem_id=' + id + '&num=' + uploadNum + '&type=' + uploadType + '&mime=' + uploadMime + '&path=' + path + '&type=' + uploadType); tableSelect.render({ elem: this, @@ -1372,23 +1576,27 @@ /** * 选择文件 */ - selectFiles: function (formObj) { - var fileSelectList = formObj !== undefined ? formObj.find("*[lay-filter='upload-select']") : $("*[lay-filter='upload-select']"); + selectFiles: function (formObj, elem) { + elem = elem || ''; + if(elem){ + var fileSelectList = elem; + }else{ + var fileSelectList = formObj !== undefined ? formObj.find("*[lay-filter='upload-select']") : $("*[lay-filter='upload-select']"); + } if (fileSelectList.length > 0) { layui.each(fileSelectList, function (i, v) { $(this).click(function (e) { - var data = $(this).data(); - if (typeof data.value == 'object') data = data.value; - uploadType = data.type, uploadNum = data.num, uploadMime = data.mime, url = data.selecturl, path = data.path; + var _t = $(this); + var dataOptions = Fun.api.getElementData(_t); + var uploadType = dataOptions.type, uploadNum = dataOptions.num, uploadMime = dataOptions.mime, url = dataOptions.selecturl, path = dataOptions.path; uploadMime = uploadMime || ''; uploadType = uploadType ? uploadType : 'radio'; uploadNum = uploadType === 'checkbox' ? uploadNum : 1; - var input = $(this).parents('.layui-upload').find('input[type="text"]'); - var token = $(this).parents('form').find('input[name="__token__"]'); - var uploadList = $(this).parents('.layui-upload').find('.layui-upload-list'); - var id = $(this).attr('id'); - console.log(uploadType) - url = url ? url : Fun.url(Upload.init.requests.select_url + '?' + + var input = _t.parents('.layui-upload').find('input[type="text"]'); + var token = _t.parents('form').find('input[name="__token__"]'); + var uploadList = _t.parents('.layui-upload').find('.layui-upload-list'); + var id = _t.attr('id'); + url = url ? url : Fun.url(Upload.init.requests.select + '?' + '&elem_id=' + id + '&num=' + uploadNum + '&type=' + uploadType + '&mime=' + uploadMime + '&path=' + path + '&type=' + uploadType); var parentiframe = Fun.api.checkLayerIframe(); @@ -1462,8 +1670,13 @@ /** * json */ - json: function (formObj) { - var jsonList = formObj !== undefined ? formObj.find("*[lay-filter='json']") : $("*[lay-filter='json']"); + json: function (formObj, elem) { + elem = elem || ''; + if(elem){ + var jsonList = elem; + }else{ + var jsonList = formObj !== undefined ? formObj.find("*[lay-filter='json']") : $("*[lay-filter='json']"); + } if (jsonList.length > 0) { require(['jsoneditor'],function(JSONEditor){ layui.each(jsonList, function (i, v) { @@ -1507,29 +1720,34 @@ }) } }, - transfer: function (formObj) { - var transferList = formObj !== undefined ? formObj.find("*[lay-filter='transfer']") : $("*[lay-filter='transfer']"); + transfer: function (formObj, elem) { + elem = elem || ''; + if(elem){ + var transferList = elem; + }else{ + var transferList = formObj !== undefined ? formObj.find("*[lay-filter='transfer']") : $("*[lay-filter='transfer']"); + } if (transferList.length > 0) { transfer = layui.transfer || parent.layui.transfer; layui.each(transferList, function (i, v) { var _t = $(this); // 配置参数 var id = _t.attr('id'); - var val = _t.data('value'); + var dataOptions = Fun.api.getElementData(_t); + var val = dataOptions.value; val = typeof val == 'string' ? val.split(','):[val]; - console.log(val) window['transfer-'+id] = transfer.render({ elem:this, id:id, - data:_t.data('data'), - title:_t.data('title'), + data:dataOptions.data, + title:dataOptions.title, value:val, - showSearch:_t.data('search') || _t.data('showsearch') ||true , - width:_t.data('width') || 200 , - height:_t.data('width') || 360 , + showSearch:dataOptions.search || dataOptions.showsearch ||true , + width:dataOptions.width || 200 , + height:dataOptions.height || 360 , text: { - none: '无数据', // 没有数据时的文案 - searchNone: '无匹配数据' // 搜索无匹配数据时的文案 + none: dataOptions.none || '无数据', // 没有数据时的文案 + searchNone: dataOptions.searchNone || '无匹配数据' // 搜索无匹配数据时的文案 },onchange: function(data, index){ var datas = transfer.getData(id); var ids = datas.map(function(item){ @@ -1575,6 +1793,7 @@ events.datepicker(form); events.editor(form); events.formarray(form); + events.select(form); events.selectcx(form); events.selectpage(form); events.autocomplete(form); diff --git a/public/static/js/require-frontend.min.js b/public/static/js/require-frontend.min.js index 3e6cc3ec4..1c64f00bc 100644 --- a/public/static/js/require-frontend.min.js +++ b/public/static/js/require-frontend.min.js @@ -34,10 +34,10 @@ var i=" ",o=e.level,r=e.dataLevel,s=e.schema[t],a=e.schemaPath+e.util.getPropert ;if(S)for(var x,k=-1,P=S.length-1;k",9:"Array"},s="UnquotedIdentifier",a="QuotedIdentifier",l="Rbracket",c="Rparen",d="Comma",u="Colon",r="Rbrace",g="Number",T="Current",E="Expref",R="Pipe",$="Flatten",p="Star",m="Filter",f="Lbrace",C="Lbracket",y="Lparen",v="Literal",I={".":"Dot","*":p,",":d,":":u,"{":f,"}":r,"]":l,"(":y,")":c,"@":T},b={"<":!0,">":!0,"=":!0,"!":!0},A={" ":!0,"\t":!0,"\n":!0};n.prototype={tokenize:function(e){var t,n,i=[];for(this._current=0;this._current"===n?"="===e[this._current]?(this._current++,{type:"GTE",value:">=",start:t}):{type:"GT",value:">",start:t}:"="===n&&"="===e[this._current]?(this._current++,{type:"EQ",value:"==",start:t}):void 0},_consumeLiteral:function(e){this._current++;for(var t=this._current,n=e.length;"`"!==e[this._current]&&this._currentNumber.MAX_SAFE_INTEGER||n=o.length)throw new SyntaxError("Unexpected end of JSON input")}var i={},n=0,r=0,s=0,a=t&&t.bigint&&"undefined"!=typeof BigInt;return{data:l("",!0),pointers:i}},t.stringify=function(e,t,n){function m(e){d+=e.length,g+=e.length,l+=e}function f(e){if(u){for(l+="\n"+y(e,u),h++,d=0;e--;)i?(h+=i,d=s):d+=s,g+=r;g+=1}}function C(e,t){c[e]=c[e]||{},c[e][t]={line:h,column:d,pos:g}}function y(e,t){return Array(e+1).join(t)}if(v(e)){var i=0;switch(_typeof(u="object"==(void 0===n?"undefined":_typeof(n))?n.space:n)){case"number":var o=101){t[0]=t[0].slice(0,-1);for(var i=t.length-1,o=1;o1&&(i=n[0]+"@",e=n[1]),e=e.replace(_,"."),i+N(e.split("."),t).join(".")}function j(e){for(var t=[],n=0,i=e.length;n=55296&&o<=56319&&n>6|192).toString(16).toUpperCase()+"%"+(63&t|128).toString(16).toUpperCase():"%"+(t>>12|224).toString(16).toUpperCase()+"%"+(t>>6&63|128).toString(16).toUpperCase()+"%"+(63&t|128).toString(16).toUpperCase()}function c(e){for(var t="",n=0,i=e.length;n=194&&o<224){if(i-n>=6){var r=parseInt(e.substr(n+4,2),16);t+=String.fromCharCode((31&o)<<6|63&r)}else t+=e.substr(n,6);n+=6}else if(o>=224){if(i-n>=9){var s=parseInt(e.substr(n+4,2),16),a=parseInt(e.substr(n+7,2),16);t+=String.fromCharCode((15&o)<<12|(63&s)<<6|63&a)}else t+=e.substr(n,9);n+=9}else t+=e.substr(n,3),n+=3}return t}function d(e,n){function t(e){var t=c(e);return t.match(n.UNRESERVED)?t:e}return e.scheme&&(e.scheme=String(e.scheme).replace(n.PCT_ENCODED,t).toLowerCase().replace(n.NOT_SCHEME,"")),void 0!==e.userinfo&&(e.userinfo=String(e.userinfo).replace(n.PCT_ENCODED,t).replace(n.NOT_USERINFO,m).replace(n.PCT_ENCODED,p)),void 0!==e.host&&(e.host=String(e.host).replace(n.PCT_ENCODED,t).toLowerCase().replace(n.NOT_HOST,m).replace(n.PCT_ENCODED,p)),void 0!==e.path&&(e.path=String(e.path).replace(n.PCT_ENCODED,t).replace(e.scheme?n.NOT_PATH:n.NOT_PATH_NOSCHEME,m).replace(n.PCT_ENCODED,p)),void 0!==e.query&&(e.query=String(e.query).replace(n.PCT_ENCODED,t).replace(n.NOT_QUERY,m).replace(n.PCT_ENCODED,p)),void 0!==e.fragment&&(e.fragment=String(e.fragment).replace(n.PCT_ENCODED,t).replace(n.NOT_FRAGMENT,m).replace(n.PCT_ENCODED,p)),e}function w(e){return e.replace(/^0*(.*)/,"$1")||"0"}function S(e,t){var n=e.match(t.IPV4ADDRESS)||[],i=A(n,2),o=i[1];return o?o.split(".").map(w).join("."):e}function ee(e,t){var n=e.match(t.IPV6ADDRESS)||[],i=A(n,3),o=i[1],r=i[2];if(o){for(var s=o.toLowerCase().split("::").reverse(),a=A(s,2),l=a[0],c=a[1],h=c?c.split(":").map(w):[],d=l.split(":").map(w),u=t.IPV4ADDRESS.test(d[d.length-1]),g=u?7:8,p=d.length-g,m=Array(g),f=0;f1){var I=m.slice(0,y.index),b=m.slice(y.index+y.length);v=I.join(":")+"::"+b.join(":")}else v=m.join(":");return r&&(v+="%"+r),v}return e}function s(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n={},i=!1!==t.iri?l:a;"suffix"===t.reference&&(e=(t.scheme?t.scheme+":":"")+"//"+e);var o=e.match(te);if(o){ne?(n.scheme=o[1],n.userinfo=o[3],n.host=o[4],n.port=parseInt(o[5],10),n.path=o[6]||"",n.query=o[7],n.fragment=o[8],isNaN(n.port)&&(n.port=o[5])):(n.scheme=o[1]||void 0,n.userinfo=-1!==e.indexOf("@")?o[3]:void 0,n.host=-1!==e.indexOf("//")?o[4]:void 0,n.port=parseInt(o[5],10),n.path=o[6]||"",n.query=-1!==e.indexOf("?")?o[7]:void 0,n.fragment=-1!==e.indexOf("#")?o[8]:void 0,isNaN(n.port)&&(n.port=e.match(/\/\/(?:.|\n)*\:(?:\/|\?|\#|$)/)?o[4]:void 0)),n.host&&(n.host=ee(S(n.host,i),i)),void 0!==n.scheme||void 0!==n.userinfo||void 0!==n.host||void 0!==n.port||n.path||void 0!==n.query?void 0===n.scheme?n.reference="relative":void 0===n.fragment?n.reference="absolute":n.reference="uri":n.reference="same-document",t.reference&&"suffix"!==t.reference&&t.reference!==n.reference&&(n.error=n.error||"URI is not a "+t.reference+" reference.");var r=h[(t.scheme||n.scheme||"").toLowerCase()];if(t.unicodeSupport||r&&r.unicodeSupport)d(n,i);else{if(n.host&&(t.domainHost||r&&r.domainHost))try{n.host=C.toASCII(n.host.replace(i.PCT_ENCODED,c).toLowerCase())}catch(e){n.error=n.error||"Host's domain name can not be converted to ASCII via punycode: "+e}d(n,a)}r&&r.parse&&r.parse(n,t)}else n.error=n.error||"URI can not be parsed.";return n}function ie(e,t){var n=!1!==t.iri?l:a,i=[];return void 0!==e.userinfo&&(i.push(e.userinfo),i.push("@")),void 0!==e.host&&i.push(ee(S(String(e.host),n),n).replace(n.IPV6ADDRESS,function(e,t,n){return"["+t+(n?"%25"+n:"")+"]"})),"number"!=typeof e.port&&"string"!=typeof e.port||(i.push(":"),i.push(String(e.port))),i.length?i.join(""):void 0}function u(e){for(var t=[];e.length;)if(e.match(oe))e=e.replace(oe,"");else if(e.match(re))e=e.replace(re,"/");else if(e.match(se))e=e.replace(se,"/"),t.pop();else if("."===e||".."===e)e="";else{var n=e.match(ae);if(!n)throw new Error("Unexpected dot segment condition");var i=n[0];e=e.slice(i.length),t.push(i)}return t.join("")}function g(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},e=n.iri?l:a,i=[],o=h[(n.scheme||t.scheme||"").toLowerCase()];if(o&&o.serialize&&o.serialize(t,n),t.host)if(e.IPV6ADDRESS.test(t.host));else if(n.domainHost||o&&o.domainHost)try{t.host=n.iri?C.toUnicode(t.host):C.toASCII(t.host.replace(e.PCT_ENCODED,c).toLowerCase())}catch(e){t.error=t.error||"Host's domain name can not be converted to "+(n.iri?"Unicode":"ASCII")+" via punycode: "+e}d(t,e),"suffix"!==n.reference&&t.scheme&&(i.push(t.scheme),i.push(":"));var r=ie(t,n);if(void 0!==r&&("suffix"!==n.reference&&i.push("//"),i.push(r),t.path&&"/"!==t.path.charAt(0)&&i.push("/")),void 0!==t.path){var s=t.path;n.absolutePath||o&&o.absolutePath||(s=u(s)),void 0===r&&(s=s.replace(/^\/\//,"/%2F")),i.push(s)}return void 0!==t.query&&(i.push("?"),i.push(t.query)),void 0!==t.fragment&&(i.push("#"),i.push(t.fragment)),i.join("")}function le(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},i=arguments[3],o={};return i||(e=s(g(e,n),n),t=s(g(t,n),n)),n=n||{},!n.tolerant&&t.scheme?(o.scheme=t.scheme,o.userinfo=t.userinfo,o.host=t.host,o.port=t.port,o.path=u(t.path||""),o.query=t.query):(void 0!==t.userinfo||void 0!==t.host||void 0!==t.port?(o.userinfo=t.userinfo,o.host=t.host,o.port=t.port,o.path=u(t.path||""),o.query=t.query):(t.path?("/"===t.path.charAt(0)?o.path=u(t.path):(void 0===e.userinfo&&void 0===e.host&&void 0===e.port||e.path?e.path?o.path=e.path.slice(0,e.path.lastIndexOf("/")+1)+t.path:o.path=t.path:o.path="/"+t.path,o.path=u(o.path)),o.query=t.query):(o.path=e.path,void 0!==t.query?o.query=t.query:o.query=e.query),o.userinfo=e.userinfo,o.host=e.host,o.port=e.port),o.scheme=e.scheme),o.fragment=t.fragment,o}function ce(e,t,n){var i=x({scheme:"null"},n);return g(le(s(e,i),s(t,i),i,!0),i)}function ue(e,t){return"string"==typeof e?e=g(s(e,t),t):"object"===i(e)&&(e=s(g(e,t),t)),e}function ge(e,t,n){return"string"==typeof e?e=g(s(e,n),n):"object"===i(e)&&(e=g(e,n)),"string"==typeof t?t=g(s(t,n),n):"object"===i(t)&&(t=g(t,n)),e===t}function pe(e,t){return e&&e.toString().replace(t&&t.iri?l.ESCAPE:a.ESCAPE,m)}function y(e,t){return e&&e.toString().replace(t&&t.iri?l.PCT_ENCODED:a.PCT_ENCODED,c)}function fe(e){return"boolean"==typeof e.secure?e.secure:"wss"===String(e.scheme).toLowerCase()}function I(e){var t=c(e);return t.match(ke)?t:e}var a=t(!1),l=t(!0),A=function(){function n(e,t){var n=[],i=!0,o=!1,r=void 0;try{for(var a,s=e[Symbol.iterator]();!(i=(a=s.next()).done)&&(n.push(a.value),!t||n.length!==t);i=!0);}catch(e){o=!0,r=e}finally{try{!i&&s.return&&s.return()}finally{if(o)throw r}}return n}return function(e,t){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return n(e,t);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),k=function(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t= 0x80 (not a basic code point)","invalid-input":"Invalid input"},O=Math.floor,V=String.fromCharCode,z=function(t){return t-48<10?t-22:t-65<26?t-65:t-97<26?t-97:B},X=function(t,n){return t+22+75*(t<26)-((0!=n)<<5)},Y=function(t,n,i){var o=0;for(t=i?O(t/700):t>>1,t+=O(t/n);t>455;o+=B)t=O(t/35);return O(o+36*t/(t+38))},J=function(t){var n=[],i=t.length,o=0,r=128,s=72,a=t.lastIndexOf("-");a<0&&(a=0);for(var l=0;l=128&&Z("not-basic"),n.push(t.charCodeAt(l));for(var c=a>0?a+1:0;c=i&&Z("invalid-input");var g=z(t.charCodeAt(c++));(g>=B||g>O((M-o)/d))&&Z("overflow"),o+=g*d;var p=u<=s?1:u>=s+26?26:u-s;if(gO(M/m)&&Z("overflow"),d*=m}var f=n.length+1;s=Y(o-h,f,0==h),O(o/f)>M-r&&Z("overflow"),r+=O(o/f),o%=f,n.splice(o++,0,r)}return String.fromCodePoint.apply(String,n)},U=function(t){var n=[];t=j(t);var i=t.length,o=128,r=0,s=72,a=!0,l=!1,c=void 0;try{for(var d,h=t[Symbol.iterator]();!(a=(d=h.next()).done);a=!0){var u=d.value;u<128&&n.push(V(u))}}catch(e){l=!0,c=e}finally{try{!a&&h.return&&h.return()}finally{if(l)throw c}}var g=n.length,p=g;for(g&&n.push("-");p=o&&bO((M-r)/A)&&Z("overflow"),r+=(m-o)*A,o=m;var w=!0,S=!1,x=void 0;try{for(var T,k=t[Symbol.iterator]();!(w=(T=k.next()).done);w=!0){var E=T.value;if(EM&&Z("overflow"),E==o){for(var R=r,$=B;;$+=B){var _=$<=s?1:$>=s+26?26:$-s;if(R<_)break;var L=R-_,N=B-_;n.push(V(X(_+L%N,0))),R=O(L/N)}n.push(V(X(R,0))),s=Y(r,A,p==g),r=0,++p}}}catch(e){S=!0,x=e}finally{try{!w&&k.return&&k.return()}finally{if(S)throw x}}++r,++o}return n.join("")},C={version:"2.1.0",ucs2:{decode:j,encode:function(t){return String.fromCodePoint.apply(String,k(t))}},decode:J,encode:U,toASCII:function(t){return D(t,function(e){return $.test(e)?"xn--"+U(e):e})},toUnicode:function(t){return D(t,function(e){return R.test(e)?J(e.slice(4).toLowerCase()):e})}},h={},te=/^(?:([^:\/?#]+):)?(?:\/\/((?:([^\/?#@]*)@)?(\[[^\/?#\]]+\]|[^\/?#:]*)(?:\:(\d*))?))?([^?#]*)(?:\?([^#]*))?(?:#((?:.|\n|\r)*))?/i,ne=void 0==="".match(/(){0}/)[1],oe=/^\.\.?\//,re=/^\/\.(\/|$)/,se=/^\/\.\.(\/|$)/,ae=/^\/?(?:.|\n)*?(?=\/|$)/,n={scheme:"http",domainHost:!0,parse:function(t,n){return t.host||(t.error=t.error||"HTTP URIs must have a host."),t},serialize:function(t,n){var i="https"===String(t.scheme).toLowerCase();return t.port!==(i?443:80)&&""!==t.port||(t.port=void 0),t.path||(t.path="/"),t}},me={scheme:"https",domainHost:n.domainHost,parse:n.parse,serialize:n.serialize},o={scheme:"ws",domainHost:!0,parse:function(t,n){var i=t;return i.secure=fe(i),i.resourceName=(i.path||"/")+(i.query?"?"+i.query:""),i.path=void 0,i.query=void 0,i},serialize:function(t,n){if(t.port!==(fe(t)?443:80)&&""!==t.port||(t.port=void 0),"boolean"==typeof t.secure&&(t.scheme=t.secure?"wss":"ws",t.secure=void 0),t.resourceName){var i=t.resourceName.split("?"),o=A(i,2),r=o[0],s=o[1];t.path=r&&"/"!==r?r:void 0,t.query=s,t.resourceName=void 0}return t.fragment=void 0,t}},Ce={scheme:"wss",domainHost:o.domainHost,parse:o.parse,serialize:o.serialize},ye={},Ie="[A-Za-z0-9\\-\\.\\_\\~\\xA0-\\u200D\\u2010-\\u2029\\u202F-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]",f="[0-9A-Fa-f]",be=de(de("%[EFef]"+f+"%"+f+f+"%"+f+f)+"|"+de("%[89A-Fa-f]"+f+"%"+f+f)+"|"+de("%"+f+f)),Se=he("[\\!\\$\\%\\'\\(\\)\\*\\+\\,\\-\\.0-9\\<\\>A-Z\\x5E-\\x7E]",'[\\"\\\\]'),ke=new RegExp(Ie,"g"),v=new RegExp(be,"g"),Te=new RegExp(he("[^]","[A-Za-z0-9\\!\\$\\%\\'\\*\\+\\-\\^\\_\\`\\{\\|\\}\\~]","[\\.]",'[\\"]',Se),"g"),Ee=new RegExp(he("[^]",Ie,"[\\!\\$\\'\\(\\)\\*\\+\\,\\;\\:\\@]"),"g"),Re=Ee,$e={scheme:"mailto",parse:function(t,n){var i=t,o=i.to=i.path?i.path.split(","):[];if(i.path=void 0,i.query){for(var r=!1,s={},a=i.query.split("&"),l=0,c=a.length;l1&&void 0!==arguments[1]?arguments[1]:1;return(t>0?e.toFixed(t).replace(/0+$/,"").replace(/\.$/,""):e.toString())||"0"}function l(e){var t=document.createElement("div");return t.innerHTML=e,t.firstElementChild}function c(e,h,d){function g(e,t,n){return Math.max(t,Math.min(e,n))}function i(e,t,n){if(n&&(u=!0),u){e.preventDefault();var i=h.getBoundingClientRect(),o=i.width,r=i.height,s=t.clientX,a=t.clientY,l=g(s-i.left,0,o),c=g(a-i.top,0,r);d(l/o,c/r)}}function t(e,t){1===(void 0===e.buttons?e.which:e.buttons)?i(e,e,t):u=!1}function n(e,t){1===e.touches.length?i(e,e.touches[0],t):u=!1}var u=!1;e.add(h,"mousedown",function(e){t(e,!0)}),e.add(h,"touchstart",function(e){n(e,!0)}),e.add(window,"mousemove",t),e.add(h,"touchmove",n),e.add(window,"mouseup",function(e){u=!1}),e.add(h,"touchend",function(e){u=!1}),e.add(h,"touchcancel",function(e){u=!1})}function w(e,t){return(t||document).querySelector(e)}function g(e){e.preventDefault(),e.stopPropagation()}function p(e,t,n,i,o){e.add(t,h,function(e){n.indexOf(e.key)>=0&&(o&&g(e),i(e))})}var s=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},e=function(){function i(e,t){for(var n=0;n.5?u/(2-a-l):u/(a+l),a){case i:c=(o-r)/u+(o1&&(i-=1),i<1/6?t+6*(n-t)*i:i<.5?n:i<2/3?t+(n-t)*(2/3-i)*6:t},d=r<.5?r*(1+o):r+o-r*o,u=2*r-d;a=h(u,d,i+1/3),l=h(u,d,i),c=h(u,d,i-1/3)}var g=[255*a,255*l,255*c].map(Math.round);return g[3]=s,g}}]),p}(),n=function(){function r(){s(this,r),this._events=[]}return e(r,[{key:"add",value:function(t,n,i){t.addEventListener(n,i,!1),this._events.push({target:t,type:n,handler:i})}},{key:"remove",value:function(n,i,o){this._events=this._events.filter(function(e){var t=!0;return n&&n!==e.target&&(t=!1),i&&i!==e.type&&(t=!1),o&&o!==e.handler&&(t=!1),t&&r._doRemove(e.target,e.type,e.handler),!t})}},{key:"destroy",value:function(){this._events.forEach(function(e){return r._doRemove(e.target,e.type,e.handler)}),this._events=[]}}],[{key:"_doRemove",value:function(t,n,i){t.removeEventListener(n,i,!1)}}]),r}(),h="keydown",d="mousedown",u="focusin",t=function(){function t(e){s(this,t),this.settings={popup:"right",layout:"default",alpha:!0,editor:!0,editorFormat:"hex",cancelButton:!1,defaultColor:"#0cf"},this._events=new n,this.onChange=null,this.onDone=null,this.onOpen=null,this.onClose=null,this.setOptions(e)}return e(t,[{key:"setOptions",value:function(t){var n=this;if(t){var i=this.settings;if(t instanceof HTMLElement)i.parent=t;else{i.parent&&t.parent&&i.parent!==t.parent&&(this._events.remove(i.parent),this._popupInited=!1),function(e,t,n){for(var i in e)n&&n.indexOf(i)>=0||(t[i]=e[i])}(t,i),t.onChange&&(this.onChange=t.onChange),t.onDone&&(this.onDone=t.onDone),t.onOpen&&(this.onOpen=t.onOpen),t.onClose&&(this.onClose=t.onClose);var r=t.color||t.colour;r&&this._setColor(r)}var s=i.parent;if(s&&i.popup&&!this._popupInited){var a=function(t){return n.openHandler(t)};this._events.add(s,"click",a),p(this._events,s,[" ","Spacebar","Enter"],a),this._popupInited=!0}else t.parent&&!i.popup&&this.show()}}},{key:"openHandler",value:function(t){if(this.show()){t&&t.preventDefault(),this.settings.parent.style.pointerEvents="none";var n=t&&t.type===h?this._domEdit:this.domElement;setTimeout(function(){return n.focus()},100),this.onOpen&&this.onOpen(this.colour)}}},{key:"closeHandler",value:function(t){var n=t&&t.type,i=!1;if(t)if(n===d||n===u){var o=(this.__containedEvent||0)+100;t.timeStamp>o&&(i=!0)}else g(t),i=!0;else i=!0;i&&this.hide()&&(this.settings.parent.style.pointerEvents="",n!==d&&this.settings.parent.focus(),this.onClose&&this.onClose(this.colour))}},{key:"movePopup",value:function(t,n){this.closeHandler(),this.setOptions(t),n&&this.openHandler()}},{key:"setColor",value:function(t,n){this._setColor(t,{silent:n})}},{key:"_setColor",value:function(t,n){if("string"==typeof t&&(t=t.trim()),t){n=n||{};var i=void 0;try{i=new r(t)}catch(e){if(n.failSilently)return;throw e}if(!this.settings.alpha){var o=i.hsla;o[3]=1,i.hsla=o}this.colour=this.color=i,this._setHSLA(null,null,null,null,n)}}},{key:"setColour",value:function(t,n){this.setColor(t,n)}},{key:"show",value:function(){if(!this.settings.parent)return!1;if(this.domElement){var n=this._toggleDOM(!0);return this._setPosition(),n}var i=this.settings.template||'
              ',o=l(i);return this.domElement=o,this._domH=w(".picker_hue",o),this._domSL=w(".picker_sl",o),this._domA=w(".picker_alpha",o),this._domEdit=w(".picker_editor input",o),this._domSample=w(".picker_sample",o),this._domOkay=w(".picker_done button",o),this._domCancel=w(".picker_cancel button",o),o.classList.add("layout_"+this.settings.layout),this.settings.alpha||o.classList.add("no_alpha"),this.settings.editor||o.classList.add("no_editor"),this.settings.cancelButton||o.classList.add("no_cancel"),this._ifPopup(function(){return o.classList.add("popup")}),this._setPosition(),this.colour?this._updateUI():this._setColor(this.settings.defaultColor),this._bindEvents(),!0}},{key:"hide",value:function(){return this._toggleDOM(!1)}},{key:"destroy",value:function(){this._events.destroy(),this.domElement&&this.settings.parent.removeChild(this.domElement)}},{key:"_bindEvents",value:function(){function s(e,t,n){r.add(e,t,n)}var n=this,i=this,o=this.domElement,r=this._events;s(o,"click",function(e){return e.preventDefault()}),c(r,this._domH,function(e,t){return i._setHSLA(e)}),c(r,this._domSL,function(e,t){return i._setHSLA(null,e,1-t)}),this.settings.alpha&&c(r,this._domA,function(e,t){return i._setHSLA(null,null,null,1-t)});var t=this._domEdit;s(t,"input",function(e){i._setColor(this.value,{fromEditor:!0,failSilently:!0})}),s(t,"focus",function(e){var t=this;t.selectionStart===t.selectionEnd&&t.select()}),this._ifPopup(function(){var e=function(t){return n.closeHandler(t)};s(window,d,e),s(window,u,e),p(r,o,["Esc","Escape"],e);var t=function(t){n.__containedEvent=t.timeStamp};s(o,d,t),s(o,u,t),s(n._domCancel,"click",e)});var a=function(t){n._ifPopup(function(){return n.closeHandler(t)}),n.onDone&&n.onDone(n.colour)};s(this._domOkay,"click",a),p(r,o,["Enter"],a)}},{key:"_setPosition",value:function(){var n=this.settings.parent,i=this.domElement;n!==i.parentNode&&n.appendChild(i),this._ifPopup(function(e){"static"===getComputedStyle(n).position&&(n.style.position="relative");var t=!0===e?"popup_right":"popup_"+e;["popup_top","popup_bottom","popup_left","popup_right"].forEach(function(e){e===t?i.classList.add(e):i.classList.remove(e)}),i.classList.add(t)})}},{key:"_setHSLA",value:function(t,n,i,o,r){r=r||{};var s=this.colour,a=s.hsla;[t,n,i,o].forEach(function(e,t){(e||0===e)&&(a[t]=e)}),s.hsla=a,this._updateUI(r),this.onChange&&!r.silent&&this.onChange(s)}},{key:"_updateUI",value:function(t){function g(e,t,n){t.style.left=100*n+"%"}function p(e,t,n){t.style.top=100*n+"%"}if(this.domElement){t=t||{};var n=this.colour,i=n.hsla,o="hsl("+360*i[0]+", 100%, 50%)",r=n.hslString,s=n.hslaString,a=this._domH,l=this._domSL,c=this._domA,h=w(".picker_selector",a),d=w(".picker_selector",l),u=w(".picker_selector",c);g(a,h,i[0]),this._domSL.style.backgroundColor=this._domH.style.color=o,g(l,d,i[1]),p(l,d,1-i[2]),l.style.color=r,p(c,u,1-i[3]);var m=r,f=m.replace("hsl","hsla").replace(")",", 0)"),C="linear-gradient("+[m,f]+")";if(this._domA.style.background=C+", linear-gradient(45deg, lightgrey 25%, transparent 25%, transparent 75%, lightgrey 75%) 0 0 / 2em 2em,\n linear-gradient(45deg, lightgrey 25%, white 25%, white 75%, lightgrey 75%) 1em 1em / 2em 2em",!t.fromEditor){var y=this.settings.editorFormat,v=this.settings.alpha,I=void 0;switch(y){case"rgb":I=n.printRGB(v);break;case"hsl":I=n.printHSL(v);break;default:I=n.printHex(v)}this._domEdit.value=I}this._domSample.style.color=s}}},{key:"_ifPopup",value:function(t,n){this.settings.parent&&this.settings.popup?t&&t(this.settings.popup):n&&n()}},{key:"_toggleDOM",value:function(t){var n=this.domElement;if(!n)return!1;var i=t?"":"none",o=n.style.display!==i;return o&&(n.style.display=i),o}}]),t}(),i=document.createElement("style");return i.textContent='.picker_wrapper.no_alpha .picker_alpha{display:none}.picker_wrapper.no_editor .picker_editor{position:absolute;z-index:-1;opacity:0}.picker_wrapper.no_cancel .picker_cancel{display:none}.layout_default.picker_wrapper{display:flex;flex-flow:row wrap;justify-content:space-between;align-items:stretch;font-size:10px;width:25em;padding:.5em}.layout_default.picker_wrapper input,.layout_default.picker_wrapper button{font-size:1rem}.layout_default.picker_wrapper>*{margin:.5em}.layout_default.picker_wrapper::before{content:"";display:block;width:100%;height:0;order:1}.layout_default .picker_slider,.layout_default .picker_selector{padding:1em}.layout_default .picker_hue{width:100%}.layout_default .picker_sl{flex:1 1 auto}.layout_default .picker_sl::before{content:"";display:block;padding-bottom:100%}.layout_default .picker_editor{order:1;width:6.5rem}.layout_default .picker_editor input{width:100%;height:100%}.layout_default .picker_sample{order:1;flex:1 1 auto}.layout_default .picker_done,.layout_default .picker_cancel{order:1}.picker_wrapper{box-sizing:border-box;background:#f2f2f2;box-shadow:0 0 0 1px silver;cursor:default;font-family:sans-serif;color:#444;pointer-events:auto}.picker_wrapper:focus{outline:none}.picker_wrapper button,.picker_wrapper input{box-sizing:border-box;border:none;box-shadow:0 0 0 1px silver;outline:none}.picker_wrapper button:focus,.picker_wrapper button:active,.picker_wrapper input:focus,.picker_wrapper input:active{box-shadow:0 0 2px 1px #1e90ff}.picker_wrapper button{padding:.4em .6em;cursor:pointer;background-color:#f5f5f5;background-image:linear-gradient(0deg, gainsboro, transparent)}.picker_wrapper button:active{background-image:linear-gradient(0deg, transparent, gainsboro)}.picker_wrapper button:hover{background-color:#fff}.picker_selector{position:absolute;z-index:1;display:block;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);border:2px solid #fff;border-radius:100%;box-shadow:0 0 3px 1px #67b9ff;background:currentColor;cursor:pointer}.picker_slider .picker_selector{border-radius:2px}.picker_hue{position:relative;background-image:linear-gradient(90deg, red, yellow, lime, cyan, blue, magenta, red);box-shadow:0 0 0 1px silver}.picker_sl{position:relative;box-shadow:0 0 0 1px silver;background-image:linear-gradient(180deg, white, rgba(255, 255, 255, 0) 50%),linear-gradient(0deg, black, rgba(0, 0, 0, 0) 50%),linear-gradient(90deg, #808080, rgba(128, 128, 128, 0))}.picker_alpha,.picker_sample{position:relative;background:linear-gradient(45deg, lightgrey 25%, transparent 25%, transparent 75%, lightgrey 75%) 0 0/2em 2em,linear-gradient(45deg, lightgrey 25%, white 25%, white 75%, lightgrey 75%) 1em 1em/2em 2em;box-shadow:0 0 0 1px silver}.picker_alpha .picker_selector,.picker_sample .picker_selector{background:none}.picker_editor input{font-family:monospace;padding:.2em .4em}.picker_sample::before{content:"";position:absolute;display:block;width:100%;height:100%;background:currentColor}.picker_arrow{position:absolute;z-index:-1}.picker_wrapper.popup{position:absolute;z-index:2;margin:1.5em}.picker_wrapper.popup,.picker_wrapper.popup .picker_arrow::before,.picker_wrapper.popup .picker_arrow::after{background:#f2f2f2;box-shadow:0 0 10px 1px rgba(0,0,0,.4)}.picker_wrapper.popup .picker_arrow{width:3em;height:3em;margin:0}.picker_wrapper.popup .picker_arrow::before,.picker_wrapper.popup .picker_arrow::after{content:"";display:block;position:absolute;top:0;left:0;z-index:-99}.picker_wrapper.popup .picker_arrow::before{width:100%;height:100%;-webkit-transform:skew(45deg);transform:skew(45deg);-webkit-transform-origin:0 100%;transform-origin:0 100%}.picker_wrapper.popup .picker_arrow::after{width:150%;height:150%;box-shadow:none}.popup.popup_top{bottom:100%;left:0}.popup.popup_top .picker_arrow{bottom:0;left:0;-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.popup.popup_bottom{top:100%;left:0}.popup.popup_bottom .picker_arrow{top:0;left:0;-webkit-transform:rotate(90deg) scale(1, -1);transform:rotate(90deg) scale(1, -1)}.popup.popup_left{top:0;right:100%}.popup.popup_left .picker_arrow{top:0;right:0;-webkit-transform:scale(-1, 1);transform:scale(-1, 1)}.popup.popup_right{top:0;left:100%}.popup.popup_right .picker_arrow{top:0;left:0}',document.documentElement.firstElementChild.appendChild(i),t.StyleElement=i,t}()},9857:function(o,e,t){"use strict";function P(e){return e>=M&&e<=B}function H(e){return m.test(e)}function G(e){return f.test(e)||e&&O(e.charCodeAt(0))}function F(e){return e===n||e===k||e===T||e===E}function O(e){return V(e)||D(e)}function V(e){return e===_||e===r||e===s}function Z(e){return e===_}function D(e){return e===i||e===a||e===l||e===g||e===p}function j(e){return e===i}function K(e,t,n){return n=2=g.length;(n||(G(g[p])||i?m=z(m,":"):l()),r())||(n||i?m+="null":l())}return g.charCodeAt(p)===u?(m+="}",p++):m=z(m,"}"),!0}()||function(){if(g.charCodeAt(p)!==A)return!1;m+="[",p++,f();for(var _e7=!0;pe&&(e=g.slice(e,p),t=/^0\d/.test(e),m+=t?'"'.concat(e,'"'):e,!0)}()||n("true","true")||n("false","false")||n("null","null")||n("True","true")||n("False","false")||n("None","null")||a();return f(),e}function f(){for(t();function(){if(g.charCodeAt(p)===c&&g.charCodeAt(p+1)===h){for(;p=oe&&n<=re||n===se||n===ae||n===le;)e+=t?g[p]:" ",p++;return 0=g.length)return o?(_t5=z(_t5,'"'),m+=_t5,!0):(p=a,m=m.substring(0,l),y(!0));if(s(g.charCodeAt(p))){var c=p,h=_t5.length;if(_t5+='"',p++,m+=_t5,f(),o||p>=g.length||H(g.charAt(p))||O(g.charCodeAt(p)))return v(),!0;if(H(g.charAt(function(e){for(var t=e;0=M&&i<=B||i>=U&&i<=te||i>=Q&&i<=ne);)_e8++;if(6===_e8)_t5+=g.slice(p,p+6),p+=6;else{if(!(p+_e8>=g.length))throw h=void 0,h=g.slice(p,p+6),new I('Invalid unicode character "'.concat(h,'"'),p);p=g.length}}else _t5+=c,p+=2;else{var d=g.charAt(p),u=g.charCodeAt(p);if(u===_&&g.charCodeAt(p-1)!==b)_t5+="\\"+d;else if((n=u)===k||n===E||n===T||n===R||n===$)_t5+=ce[d];else{if(!(32<=(n=u)&&n<=1114111))throw u=void 0,u=d,new I("Invalid character "+JSON.stringify(u),p);_t5+=d}p++}}r&&C()}}return!1}function v(){for(f();g.charCodeAt(p)===L;){!0,p++,f();var t=(m=K(m,'"',!0)).length,n=y();m=n?(n=m,t=t,i=1,n.substring(0,t)+n.substring(t+i)):z(m,'"')}var i}function n(e,t){return g.slice(p,p+e.length)===e&&(m+=t,p+=e.length,!0)}function a(){for(var e=p;pe){if(g.charCodeAt(p)===S)p++,r(),g.charCodeAt(p)===x&&(p++,g.charCodeAt(p)===J)&&p++;else{for(;F(g.charCodeAt(p-1))&&0=g.length)return m+=g.slice(e,p)+"0",1;i(e)}function l(){throw new I("Colon expected",p)}var p=0,m="";if(!r())throw new I("Unexpected end of json string",g.length);var e=s(W);if(e&&f(),G(g[p])&&/[,\n][ \t\r]*$/.test(m)){e||(m=z(m,","));for(var _e5=!0,_t3=!0;_t3;)_e5?_e5=!1:s(W)||(m=z(m,",")),_t3=r();_t3||(m=K(m,",")),m="[\n".concat(m,"\n]")}else e&&(m=K(m,","));for(;g.charCodeAt(p)===u||g.charCodeAt(p)===w;)p++,f();if(p>=g.length)return m;throw new I("Unexpected character "+JSON.stringify(g[p]),p)}t.d(e,{m:function(){return C}});var I=function(_Error){function I(e,t){var _this;return _classCallCheck(this,I),_this=_possibleConstructorReturn(this,(I.__proto__||Object.getPrototypeOf(I)).call(this,e+" at position "+t)),_this.position=t,_this}return _inherits(I,_Error),I}(Error),b=92,c=47,h=42,d=123,u=125,A=91,w=93,S=40,x=41,n=32,k=10,T=9,E=13,R=8,$=12,_=34,L=43,N=45,i=39,M=48,B=57,W=44,X=46,Y=58,J=59,U=65,Q=97,q=69,ee=101,te=70,ne=102,ie=160,oe=8192,re=8202,se=8239,ae=8287,le=12288,r=8220,s=8221,a=8216,l=8217,g=96,p=180,m=/^[,:[\]/{}()\n+]$/,f=/^[[{\w-]$/,ce={"\b":"\\b","\f":"\\f","\n":"\\n","\r":"\\r","\t":"\\t"},he={'"':'"',"\\":"\\","/":"/",b:"\b",f:"\f",n:"\n",r:"\r",t:"\t"}},3420:function(e){"use strict";e.exports=JSON.parse('{"$schema":"http://json-schema.org/draft-07/schema#","$id":"https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#","description":"Meta-schema for $data reference (JSON Schema extension proposal)","type":"object","required":["$data"],"properties":{"$data":{"type":"string","anyOf":[{"format":"relative-json-pointer"},{"format":"json-pointer"}]}},"additionalProperties":false}')},5207:function(e){"use strict";e.exports=JSON.parse('{"id":"http://json-schema.org/draft-04/schema#","$schema":"http://json-schema.org/draft-04/schema#","description":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"positiveInteger":{"type":"integer","minimum":0},"positiveIntegerDefault0":{"allOf":[{"$ref":"#/definitions/positiveInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"minItems":1,"uniqueItems":true}},"type":"object","properties":{"id":{"type":"string"},"$schema":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"default":{},"multipleOf":{"type":"number","minimum":0,"exclusiveMinimum":true},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"boolean","default":false},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"boolean","default":false},"maxLength":{"$ref":"#/definitions/positiveInteger"},"minLength":{"$ref":"#/definitions/positiveIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"anyOf":[{"type":"boolean"},{"$ref":"#"}],"default":{}},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":{}},"maxItems":{"$ref":"#/definitions/positiveInteger"},"minItems":{"$ref":"#/definitions/positiveIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"maxProperties":{"$ref":"#/definitions/positiveInteger"},"minProperties":{"$ref":"#/definitions/positiveIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"anyOf":[{"type":"boolean"},{"$ref":"#"}],"default":{}},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"enum":{"type":"array","minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"dependencies":{"exclusiveMaximum":["maximum"],"exclusiveMinimum":["minimum"]},"default":{}}')},6801:function(e){"use strict";e.exports=JSON.parse('{"$schema":"http://json-schema.org/draft-06/schema#","$id":"http://json-schema.org/draft-06/schema#","title":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"nonNegativeInteger":{"type":"integer","minimum":0},"nonNegativeIntegerDefault0":{"allOf":[{"$ref":"#/definitions/nonNegativeInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"uniqueItems":true,"default":[]}},"type":["object","boolean"],"properties":{"$id":{"type":"string","format":"uri-reference"},"$schema":{"type":"string","format":"uri"},"$ref":{"type":"string","format":"uri-reference"},"title":{"type":"string"},"description":{"type":"string"},"default":{},"examples":{"type":"array","items":{}},"multipleOf":{"type":"number","exclusiveMinimum":0},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"number"},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"number"},"maxLength":{"$ref":"#/definitions/nonNegativeInteger"},"minLength":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"$ref":"#"},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":{}},"maxItems":{"$ref":"#/definitions/nonNegativeInteger"},"minItems":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"contains":{"$ref":"#"},"maxProperties":{"$ref":"#/definitions/nonNegativeInteger"},"minProperties":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"$ref":"#"},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"propertyNames":{"$ref":"#"},"const":{},"enum":{"type":"array","minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"default":{}}')},8198:function(e){"use strict";e.exports=JSON.parse('{"$schema":"http://json-schema.org/draft-07/schema#","$id":"http://json-schema.org/draft-07/schema#","title":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"nonNegativeInteger":{"type":"integer","minimum":0},"nonNegativeIntegerDefault0":{"allOf":[{"$ref":"#/definitions/nonNegativeInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"uniqueItems":true,"default":[]}},"type":["object","boolean"],"properties":{"$id":{"type":"string","format":"uri-reference"},"$schema":{"type":"string","format":"uri"},"$ref":{"type":"string","format":"uri-reference"},"$comment":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"default":true,"readOnly":{"type":"boolean","default":false},"examples":{"type":"array","items":true},"multipleOf":{"type":"number","exclusiveMinimum":0},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"number"},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"number"},"maxLength":{"$ref":"#/definitions/nonNegativeInteger"},"minLength":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"$ref":"#"},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":true},"maxItems":{"$ref":"#/definitions/nonNegativeInteger"},"minItems":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"contains":{"$ref":"#"},"maxProperties":{"$ref":"#/definitions/nonNegativeInteger"},"minProperties":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"$ref":"#"},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"propertyNames":{"format":"regex"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"propertyNames":{"$ref":"#"},"const":true,"enum":{"type":"array","items":true,"minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"contentMediaType":{"type":"string"},"contentEncoding":{"type":"string"},"if":{"$ref":"#"},"then":{"$ref":"#"},"else":{"$ref":"#"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"default":true}')}},o={},i.amdD=function(){throw new Error("define cannot be used indirect")},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,{a:t}),t},i.d=function(e,t){for(var n in t)i.o(t,n)&&!i.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.nmd=function(e){return e.paths=[],e.children||(e.children=[]),e},i(1346);var n,o}),layui.define(["table","jquery","form"],function(exports){"use strict";var $=layui.jquery,table=layui.table,form=layui.form,tableSelect=function(){this.v="1.1.0"};tableSelect.prototype.render=function(opt){var elem=$(opt.elem),tableDone=opt.table.done||function(){};opt.searchKey=opt.searchKey||"keyword",opt.searchPlaceholder=opt.searchPlaceholder||"关键词搜索",opt.checkedKey=opt.checkedKey||"",opt.table.page=opt.table.page||!0,opt.table.height=opt.height||315,opt.width=opt.width||"530",opt.searchType=opt.searchType||"1",opt.searchList=opt.searchList||[{key:opt.searchKey,placeholder:opt.searchPlaceholder}],elem.off("click").on("click",function(e){function setChecked(res,curr,count){for(var i=0;i=1)return!1;var t=elem.offset().top+elem.outerHeight()+"px",l=elem.offset().left+"px",tableName="tableSelect_table_"+(new Date).getTime(),tableBox='
              ';tableBox+='
              ',tableBox+='
              ',2==opt.searchType?$.each(opt.searchList,function(index,item){tableBox+='' -}):tableBox+='',tableBox+='',tableBox+="
              ",tableBox+='',tableBox+="
              ",tableBox+='
              ',tableBox+="
              ",tableBox=$(tableBox),$("body").append(tableBox);var checkedData=[];opt.table.elem="#"+tableName,opt.table.id=tableName,opt.table.done=function(res,curr,count){defaultChecked(res,curr,count),setChecked(res,curr,count),tableDone(res,curr,count)};var tableSelect_table=table.render(opt.table);table.on("radio("+tableName+")",function(obj){opt.checkedKey&&(checkedData=table.checkStatus(tableName).data),updataButton(table.checkStatus(tableName).data.length)}),table.on("checkbox("+tableName+")",function(obj){if(opt.checkedKey){if(obj.checked)for(var i=0;i$(window).height(),overWidth=elem.offset().left+tableBox.outerWidth()>$(window).width();overHeight&&tableBox.css({top:"auto",bottom:"0px"}),overWidth&&tableBox.css({left:"auto",right:"5px"}),form.on("submit(tableSelect_btn_search)",function(data){return tableSelect_table.reload({where:data.field,page:{curr:1}}),!1}),table.on("rowDouble("+tableName+")",function(obj){selectDone({data:[obj.data]})}),tableBox.find(".tableSelect_btn_select").on("click",function(){var checkStatus=table.checkStatus(tableName);checkedData.length>1&&(checkStatus.data=checkedData),selectDone(checkStatus)}),$(document).mouseup(function(e){id=$(opt.elem).attr("id");var userSet_con=$(id+",.tableSelect");userSet_con.is(e.target)||0!==userSet_con.has(e.target).length||(tableBox.remove(),delete table.cache[tableName],checkedData=[])})})},tableSelect.prototype.hide=function(opt){$(".tableSelect").remove()};var tableSelect=new tableSelect;window.top==window.self&&$(window).scroll(function(){tableSelect.hide()}),exports("tableSelect",tableSelect)}),define("tableSelect",function(){}),define("plugins/require-css/css.min!plugins/lay-module/tableFilter/tableFilter",[],function(){}),layui.define(["table","jquery","form","laydate"],function(exports){var $=layui.jquery,table=layui.table,form=layui.form,laydate=layui.laydate,tableFilter={v:"1.1.0"};tableFilter.cache={},tableFilter.render=function(opt){var elem=$(opt.elem||"#table"),elemId=elem.attr("id")||"table_"+(new Date).getTime(),filters=opt.filters||[],parent=opt.parent||"body",mode=opt.mode||"local";opt.done=opt.done||function(){},tableFilter.cache[elemId]={};var main=function(){if("local"==mode){var trsIndex=tableFilter.getShowTrIndex(elem,elemId,filters);if(trsIndex.length>0){elem.next().find(".layui-table-body tr").each(function(i,tr){-1!=$.inArray($(tr).data("index"),trsIndex)?$(tr).removeClass("layui-hide"):$(tr).addClass("layui-hide")})}else elem.next().find(".layui-table-body tr").removeClass("layui-hide");tableFilter.fixAll(elem),table.resize(elemId)}layui.each(filters,function(i,filter){var filterField=filter.field,filterName=filter.name||filter.field,filterType=filter.type||"input",filterData=filter.data||[],filterUrl=filter.url||"",th=elem.next().find('.layui-table-header th[data-field="'+filterField+'"]'),icon="input"==filterType?"layui-icon-search":"layui-icon-down",filterIcon=$('');th.find(".layui-table-cell").append(filterIcon),tableFilter.cache[elemId][filterName]?filterIcon.addClass("tableFilter-has"):filterIcon.removeClass("tableFilter-has"),filterIcon.on("click",function(e){e.stopPropagation(),filter.data||filterUrl||"input"==filterType||(filterData=tableFilter.eachTds(elem,filterField));var t=$(this).offset().top+$(parent).scrollTop()+$(this).outerHeight()+5+"px",l_fix="date"==filterType?530:164,l=$(this).offset().left-($("body").outerWidth(!0)-$(parent).outerWidth(!0))-l_fix+"px",filterBox=$('
              ');if("input"==filterType&&filterBox.find("form").append(''),"checkbox"==filterType&&(filterBox.find("form").append("
                "),filterUrl||layui.each(filterData,function(i,item){filterBox.find("ul").append('
              • ')})),"radio"==filterType&&(filterBox.find("form").append('
                  '),filterUrl||(filterBox.find("ul").append('
                • '),layui.each(filterData,function(i,item){filterBox.find("ul").append('
                • ')}))),"date"==filterType&&(filterBox.find("form").append('
                  '),filterBox.find("form").append('')),filterBox.find("form").append(''),filterBox.find("form").append(''),$(this).hasClass("tableFilter-has")&&filterBox.find(".filter-del").removeClass("layui-btn-disabled").removeAttr("disabled","disabled"),$(parent).append(filterBox),form.val("table-filter-form",tableFilter.toLayuiFrom(elemId,filterName,filterType)),form.render(null,"table-filter-form"),"date"==filterType&&laydate.render({elem:".layui-table-filter-date",range:!0,type:"date",value:$(".layui-table-filter-date").next().val(),position:"static",showBottom:!1,change:function(value,date,endDate){$(".layui-table-filter-date").next().val(value)}}),filterBox.find('form input[type="search"]').focus().select(),("checkbox"==filterType||"radio"==filterType)&&filterUrl){var filterBoxUl=filterBox.find(".layui-table-filter-box ul");filterBoxUl.append('
                  '),$.getJSON(filterUrl+"?_t="+(new Date).getTime(),function(res,status,xhr){filterBoxUl.empty(),"radio"==filterType&&filterBoxUl.append('
                • '),layui.each(res.data,function(i,item){"checkbox"==filterType&&filterBoxUl.append('
                • '),"radio"==filterType&&filterBoxUl.append('
                • ')}),form.render(null,"table-filter-form"),form.val("table-filter-form",tableFilter.toLayuiFrom(elemId,filterName,filterType))})}form.on("submit(tableFilter)",function(data){if("checkbox"==filterType){var NEWfield=[];for(var key in data.field)NEWfield.push(data.field[key]);data.field[filterName]=NEWfield}if(tableFilter.cache[elemId][filterName]=data.field[filterName],tableFilter.cache[elemId][filterName].length>0?filterIcon.addClass("tableFilter-has"):filterIcon.removeClass("tableFilter-has"),"local"==mode){var trsIndex=tableFilter.getShowTrIndex(elem,elemId,filters);if(trsIndex.length>0||data.field[filterName].length>0){elem.next().find(".layui-table-body tr").each(function(i,tr){-1!=$.inArray($(tr).data("index"),trsIndex)?$(tr).removeClass("layui-hide"):$(tr).addClass("layui-hide")})}else elem.next().find(".layui-table-body tr").removeClass("layui-hide");tableFilter.updataTotal(elem),tableFilter.upNumbers(elem),tableFilter.uncheck(elem),table.resize(elemId)}else if("api"==mode){var formatFilter={},formatOp={};for(var key in tableFilter.cache[elemId]){var filterKey=key,filterValue=tableFilter.cache[elemId][key];formatFilter[filterKey]=filterValue,formatOp[filterKey]="LIKE",$.isArray(filterValue)?(formatFilter[filterKey]=filterValue,formatOp[filterKey]="IN"):formatFilter[filterKey]=filterValue}$filter={filter:JSON.stringify(formatFilter),op:JSON.stringify(formatOp)},table.reload(elemId,{where:$filter})}return opt.done(tableFilter.cache[elemId]),filterBox.remove(),!1}),filterBox.find(".layui-table-filter-box .filter-del").on("click",function(e){if(delete tableFilter.cache[elemId][filterName],filterIcon.removeClass("tableFilter-has"),"local"==mode){var trsIndex=tableFilter.getShowTrIndex(elem,elemId,filters);if(trsIndex.length>0){elem.next().find(".layui-table-body tr").each(function(i,tr){-1!=$.inArray($(tr).data("index"),trsIndex)?$(tr).removeClass("layui-hide"):$(tr).addClass("layui-hide")})}else elem.next().find(".layui-table-body tr").removeClass("layui-hide");tableFilter.updataTotal(elem),tableFilter.upNumbers(elem),tableFilter.uncheck(elem),table.resize(elemId)}else if("api"==mode){var where={};where[filterName]="",table.reload(elemId,{where:where})}opt.done(tableFilter.cache[elemId]),filterBox.remove()}),$(document).mouseup(function(e){var userSet_con=$(".layui-table-filter-view");userSet_con.is(e.target)||0!==userSet_con.has(e.target).length||filterBox.remove()})})})};return main(),{config:opt,reload:function(opt){main(),tableFilter.upNumbers(elem)}}},tableFilter.eachTds=function(elem,filterField){var tdsText=[],tdsArray=[];return elem.next().find('.layui-table-body td[data-field="'+filterField+'"]').each(function(i,td){tdsText.push($.trim(td.innerText))}),tdsText=tableFilter.tool.uniqueObjArray(tdsText),layui.each(tdsText,function(i,item){tdsArray.push({key:item,value:item})}),tdsArray},tableFilter.getShowTrIndex=function(elem,elemId,filters){var trsIndex=[],filterValues=tableFilter.cache[elemId];for(var key in filterValues){var filterKey=key,filterValue=filterValues[key];layui.each(filters,function(i,item){filterKey==item.name&&(filterKey=item.field)});var tds=elem.next().find('.layui-table-body td[data-field="'+filterKey+'"]'),this_trsIndex=[];tds.each(function(i,td){if($.isArray(filterValue))$.inArray($.trim(td.innerText),filterValue)>=0&&filterValue&&filterValue.length>0&&this_trsIndex.push($(td).parent().data("index"));else if(filterValue.indexOf(" - ")>=0){var d=$.trim(td.innerText),s=filterValue.split(" - ")[0],e=filterValue.split(" - ")[1];tableFilter.tool.isDuringDate(d,s,e)&&this_trsIndex.push($(td).parent().data("index"))}else $.trim(td.innerText).indexOf(filterValue)>=0&&this_trsIndex.push($(td).parent().data("index"))}),trsIndex=trsIndex.length<=0?this_trsIndex:this_trsIndex.length>0?tableFilter.tool.getSameArray(trsIndex,this_trsIndex):$.isArray(filterValue)?trsIndex:[]}return tableFilter.tool.uniqueObjArray(trsIndex)},tableFilter.toLayuiFrom=function(elemId,filterName,filterType){var form_val=JSON.stringify(tableFilter.cache[elemId]);return form_val=JSON.parse(form_val),"checkbox"==filterType&&(layui.each(form_val[filterName],function(i,value){form_val[filterName+"["+value+"]"]=!0}),delete form_val[filterName]),form_val},tableFilter.updataTotal=function(elem){var elemId=elem.attr("id");table.eachCols(elemId,function(i,item){if(item.totalRow){var tdAllnum=0;elem.next().find('.layui-table-body td[data-field="'+item.field+'"]').each(function(i,td){$(td).parent().hasClass("layui-hide")||(tdAllnum=(10*tdAllnum+10*Number($.trim(td.innerText)))/10)});elem.next().find('.layui-table-total td[data-field="'+item.field+'"]').each(function(i,td){$(td).find(".layui-table-cell").html(tdAllnum||"0")})}})},tableFilter.upNumbers=function(elem){var cur=elem.next().find(".layui-laypage-curr").text();cur=Number(cur||"1");var limit=elem.next().find(".layui-laypage-limits select").val();limit=Number(limit);var trs=elem.next().find(".layui-table-main tr"),n=1==cur?0:limit*(cur-1);if(trs.each(function(i,tr){$(tr).hasClass("layui-hide")||(n+=1,$(tr).find(".laytable-cell-numbers").html(n))}),elem.next().find(".layui-table-fixed").length>=1){var trs_f=elem.next().find(".layui-table-fixed .layui-table-body tr"),n_f=1==cur?0:limit*(cur-1);trs_f.each(function(i,tr_f){$(tr_f).hasClass("layui-hide")||(n_f+=1,$(tr_f).find(".laytable-cell-numbers").html(n_f))})}},tableFilter.uncheck=function(elem){elem.attr("id"),elem.attr("lay-filter");elem.next().find(".layui-table-fixed-l tr").each(function(i,tr){$(tr).find("[name='layTableCheckbox']").prop("checked")&&$(tr).find(".layui-form-checked i").click()})},tableFilter.fixAll=function(elem){var elemId=elem.attr("id"),tableName=elem.attr("lay-filter"),trs=elem.next().find(".layui-table-main tr");table.on("checkbox("+tableName+")",function(obj){if("all"==obj.type){var data=table.cache[elemId];trs.each(function(i,tr){$(tr).hasClass("layui-hide")&&(data[i].LAY_CHECKED=!1)})}})},tableFilter.hide=function(){$(".layui-table-filter-view").remove()},tableFilter.tool={uniqueObjArray:function(arr,type){var newArr=[],tArr=[];if(0==arr.length)return arr;if(type){for(var i=0;i=beginDate&&curDate<=endDate}},exports("tableFilter",tableFilter)}),define("tableFilter",["css!plugins/lay-module/tableFilter/tableFilter.css"],function(){}),layui.define(["laypage","form"],function(exports){"use strict";var IconPicker=function(){this.v="0.1.beta"},$=layui.jquery;layui.laypage,layui.form;IconPicker.prototype.render=function(options){var opts=options,elem=opts.elem,type=null==opts.type?"fontClass":opts.type,page=opts.page,limit=null==limit?12:opts.limit,search=null==opts.search||opts.search,click=opts.click,data={},tmp=(new Date).getTime(),TITLE_ID="layui-select-title-"+tmp,ICON_BODY="layui-iconpicker-"+tmp,PICKER_BODY="layui-iconpicker-body-"+tmp,PAGE_ID="layui-iconpicker-page-"+tmp,LIST_BOX="layui-iconpicker-list-box",selected="layui-form-selected",value=options.value||$(elem).val()||"layui-icon layui-icon-circle-dot",i=' '+value+"";console.log(i);var a={init:function(){return data=common.getData[type](),a.hideElem().createSelect().createBody().toggleSelect(),common.loadCss(),a},hideElem:function(){return $(elem).hide(),a},createSelect:function(){var selectHtml='
                  '+i+'
                  123
                  ';return $(elem).after(selectHtml),a},toggleSelect:function(){var item="#"+TITLE_ID+" .layui-iconpicker-item,#"+TITLE_ID+" .layui-iconpicker-item .layui-edge";return a.event("click",item,function(e){var $icon=$("#"+ICON_BODY);$icon.hasClass(selected)?$icon.removeClass(selected).addClass("layui-unselect"):$icon.addClass(selected).removeClass("layui-unselect"),e.stopPropagation()}),a},createBody:function(){var searchHtml="";search&&(searchHtml='');var bodyHtml='
                  '+searchHtml+'
                  ';return $("#"+ICON_BODY).find(".layui-anim").eq(0).html(bodyHtml),a.search().createList().check().page(),a},createList:function(text){for(var d=data,l=d.length,pageHtml="",listHtml=$('
                  '),_limit=limit,_pages=l%_limit==0?l/_limit:parseInt(l/_limit+1),icons=[],i=0;i';icon+='',icon+="
                  ",icons.push(icon)}}l=icons.length,_pages=l%_limit==0?l/_limit:parseInt(l/_limit+1);for(var i=0;i<_pages;i++){for(var lm=$('
                  '),j=i*_limit;j<(i+1)*_limit&&j无数据

                  '),page&&($("#"+PICKER_BODY).addClass("layui-iconpicker-body-page"),pageHtml='
                  1/'+_pages+' ('+l+')
                  '),$("#"+ICON_BODY).find(".layui-anim").find("."+LIST_BOX).html("").append(listHtml).append(pageHtml),a},page:function(){var icon="#"+PAGE_ID+" .layui-iconpicker-page-operate .layui-icon";return $(icon).unbind("click"),a.event("click",icon,function(e){var elem=e.currentTarget,total=parseInt($("#"+PAGE_ID+"-pages").html()),isPrev=void 0!==$(elem).attr("prev"),$cur=(parseInt($(elem).attr("data-index")),$("#"+PAGE_ID+"-current")),current=parseInt($cur.html());isPrev&¤t>1?(current-=1,$(icon+"[prev]").attr("data-index",current)):!isPrev&¤t.layui-iconpicker {max-width: 280px;}.layui-iconpicker .layui-anim{display:none;position:absolute;left:0;top:42px;padding:5px 0;z-index:899;min-width:100%;border:1px solid #d2d2d2;max-height:300px;overflow-y:auto;background-color:#fff;border-radius:2px;box-shadow:0 2px 4px rgba(0,0,0,.12);box-sizing:border-box;}.layui-iconpicker-item{border:1px solid #e6e6e6;height:38px;border-radius:4px;cursor:pointer;position:relative;}.layui-iconpicker-icon{border-right:1px solid #e6e6e6;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;width:85%;height:100%;float:left;text-align:center;background:#fff;transition:all .3s;}.layui-iconpicker-icon i{line-height:38px;font-size:18px;}.layui-iconpicker-item:hover{border-color:#D2D2D2!important;}.layui-iconpicker-item:hover .layui-iconpicker-icon{border-color:#D2D2D2!important;}.layui-iconpicker.layui-form-selected .layui-anim{display:block;}.layui-iconpicker-body{padding:6px;}.layui-iconpicker .layui-iconpicker-list{background-color:#fff;border:1px solid #ccc;border-radius:4px;}.layui-iconpicker .layui-iconpicker-icon-item{display:inline-block;width:21.1%;line-height:36px;text-align:center;cursor:pointer;vertical-align:top;height:36px;margin:4px;border:1px solid #ddd;border-radius:2px;transition:300ms;}.layui-iconpicker .layui-iconpicker-icon-item i.layui-icon{font-size:17px;}.layui-iconpicker .layui-iconpicker-icon-item:hover{background-color:#eee;border-color:#ccc;-webkit-box-shadow:0 0 2px #aaa,0 0 2px #fff inset;-moz-box-shadow:0 0 2px #aaa,0 0 2px #fff inset;box-shadow:0 0 2px #aaa,0 0 2px #fff inset;text-shadow:0 0 1px #fff;}.layui-iconpicker-search{position:relative;margin:0 0 6px 0;border:1px solid #e6e6e6;border-radius:2px;transition:300ms;}.layui-iconpicker-search:hover{border-color:#D2D2D2!important;}.layui-iconpicker-search .layui-input{cursor:text;display:inline-block;width:86%;border:none;padding-right:0;margin-top:1px;}.layui-iconpicker-search .layui-icon{position:absolute;top:11px;right:4%;}.layui-iconpicker-tips{text-align:center;padding:8px 0;cursor:not-allowed;}.layui-iconpicker-page{margin-top:6px;margin-bottom:-6px;font-size:12px;padding:0 2px;}.layui-iconpicker-page-count{display:inline-block;}.layui-iconpicker-page-operate{display:inline-block;float:right;cursor:default;}.layui-iconpicker-page-operate .layui-icon{font-size:12px;cursor:pointer;}.layui-iconpicker-body-page .layui-iconpicker-icon-limit{display:none;}.layui-iconpicker-body-page .layui-iconpicker-icon-limit:first-child{display:block;}')},getData:{fontClass:function(){return["layui-icon-github","layui-icon-moon","layui-icon-error","layui-icon-success","layui-icon-question","layui-icon-lock","layui-icon-eye","layui-icon-eye-invisible","layui-icon-clear","layui-icon-backspace","layui-icon-disabled","layui-icon-tips-fill","layui-icon-test","layui-icon-music","layui-icon-chrome","layui-icon-firefox","layui-icon-edge","layui-icon-ie","layui-icon-heart-fill","layui-icon-heart","layui-icon-light","layui-icon-time","layui-icon-bluetooth","layui-icon-at","layui-icon-mute","layui-icon-mike","layui-icon-key","layui-icon-gift","layui-icon-email","layui-icon-rss","layui-icon-wifi","layui-icon-logout","layui-icon-android","layui-icon-ios","layui-icon-windows","layui-icon-transfer","layui-icon-service","layui-icon-subtraction","layui-icon-addition","layui-icon-slider","layui-icon-print","layui-icon-export","layui-icon-cols","layui-icon-screen-restore","layui-icon-screen-full","layui-icon-rate-half","layui-icon-rate","layui-icon-rate-solid","layui-icon-cellphone","layui-icon-vercode","layui-icon-login-wechat","layui-icon-login-qq","layui-icon-login-weibo","layui-icon-password","layui-icon-username","layui-icon-refresh-3","layui-icon-auz","layui-icon-spread-left","layui-icon-shrink-right","layui-icon-snowflake","layui-icon-tips","layui-icon-note","layui-icon-home","layui-icon-senior","layui-icon-refresh","layui-icon-refresh-1","layui-icon-flag","layui-icon-theme","layui-icon-notice","layui-icon-website","layui-icon-console","layui-icon-face-surprised","layui-icon-set","layui-icon-template-1","layui-icon-app","layui-icon-template","layui-icon-praise","layui-icon-tread","layui-icon-male","layui-icon-female","layui-icon-camera","layui-icon-camera-fill","layui-icon-more","layui-icon-more-vertical","layui-icon-rmb","layui-icon-dollar","layui-icon-diamond","layui-icon-fire","layui-icon-return","layui-icon-location","layui-icon-read","layui-icon-survey","layui-icon-face-smile","layui-icon-face-cry","layui-icon-cart-simple","layui-icon-cart","layui-icon-next","layui-icon-prev","layui-icon-upload-drag","layui-icon-upload","layui-icon-download-circle","layui-icon-component","layui-icon-file-b","layui-icon-user","layui-icon-find-fill","layui-icon-loading","layui-icon-loading-1","layui-icon-add-1","layui-icon-play","layui-icon-pause","layui-icon-headset","layui-icon-video","layui-icon-voice","layui-icon-speaker","layui-icon-fonts-del","layui-icon-fonts-code","layui-icon-fonts-html","layui-icon-fonts-strong","layui-icon-unlink","layui-icon-picture","layui-icon-link","layui-icon-face-smile-b","layui-icon-align-left","layui-icon-align-right","layui-icon-align-center","layui-icon-fonts-u","layui-icon-fonts-i","layui-icon-tabs","layui-icon-radio","layui-icon-circle","layui-icon-edit","layui-icon-share","layui-icon-delete","layui-icon-form","layui-icon-cellphone-fine","layui-icon-dialogue","layui-icon-fonts-clear","layui-icon-layer","layui-icon-date","layui-icon-water","layui-icon-code-circle","layui-icon-carousel","layui-icon-prev-circle","layui-icon-layouts","layui-icon-util","layui-icon-templeate-1","layui-icon-upload-circle","layui-icon-tree","layui-icon-table","layui-icon-chart","layui-icon-chart-screen","layui-icon-engine","layui-icon-triangle-d","layui-icon-triangle-r","layui-icon-file","layui-icon-set-sm","layui-icon-reduce-circle","layui-icon-add-circle","layui-icon-404","layui-icon-about","layui-icon-up","layui-icon-down","layui-icon-left","layui-icon-right","layui-icon-circle-dot","layui-icon-search","layui-icon-set-fill","layui-icon-group","layui-icon-friends","layui-icon-reply-fill","layui-icon-menu-fill","layui-icon-log","layui-icon-picture-fine","layui-icon-face-smile-fine","layui-icon-list","layui-icon-release","layui-icon-ok","layui-icon-help","layui-icon-chat","layui-icon-top","layui-icon-star","layui-icon-star-fill","layui-icon-close-fill","layui-icon-close","layui-icon-ok-circle","layui-icon-add-circle-fine"]}}};return a.init(),new IconPicker},IconPicker.prototype.checkIcon=function(filter,iconName){var p=$("*[lay-filter="+filter+"]").next().find(".layui-iconpicker-item .layui-icon"),c=iconName;c.indexOf("#xe")>0?p.html(c):p.html("").attr("class",""+c)},exports("iconPicker",new IconPicker)}),define("iconPicker",function(){}),function(global,factory){"object"===("undefined"==typeof exports?"undefined":_typeof(exports))&&"undefined"!=typeof module?module.exports=factory():"function"==typeof define&&define.amd?define("toastr",factory):(global=global||self,global.notify=factory())}(void 0,function(){function c(args,children){var el=document.createElement("div");for(var key in args){var element=args[key];"className"==key?(key="class",el.setAttribute(key,element)):"_"==key[0]&&el.addEventListener(key.slice(1),element)}if("string"==typeof children)el.innerHTML=children;else if("object"==_typeof(children)&&children.tagName)el.appendChild(children);else if(children)for(var i=0;i-1}function removeClass(el,s){var c=el.className||"";if(hasClass(c,s)){var arr=c.split(/\s+/),i=arr.indexOf(s);arr.splice(i,1),el.className=arr.join(" ")}""===el.className&&el.removeAttribute("class")}function initConfig(obj,type){var args={};for(var key in initArgs)args[key]=initArgs[key];for(var posArr=["bottomRight","bottomLeft","topRight","topLeft","topCenter","bottomCenter","center"],i=0;i-1?args.position=it:args.msg=it:"boolean"==typeof it?args.showClose=it:"function"==typeof it?it.call(this):"number"==typeof it&&1!=i&&(args.duration=it))}return args.type=type,createMsgEl(args)}function createMsgEl(args){var _msgWrapper,type=args.type,duration=args.duration,msg=args.msg,position=args.position,closable=args.showClose,onClose=args.onClose,iconObj=getIconObj();document.getElementsByClassName(position)[0]?_msgWrapper=document.getElementsByClassName(position)[0]:(_msgWrapper=c({className:"notify-msg-stage "+position}),msgWrappers.push(_msgWrapper)),"loading"===type&&(msg=""===msg?"正在加载,请稍后":msg,closable=!1);var el;el=-1!=positionB.indexOf(position)?c({className:"notify-msg-wrapper"},[c({className:"notify-msg notify-bottom notify-msg-fade-in-b "+type},[c({className:"notify-msg-icon"},iconObj[type]),c({className:"notify-msg-content"},msg),c({className:"notify-msg-wait "+(closable?"notify-msg-pointer":""),_click:function(){closable&&(closeFlag=!0,flag=!1,closeMsg(el,onClose,_msgWrapper))}},getMsgRight(closable))])]):c({className:"notify-msg-wrapper"},[c({className:"notify-msg notify-msg-fade-in "+type},[c({className:"notify-msg-icon"},iconObj[type]),c({className:"notify-msg-content"},msg),c({className:"notify-msg-wait "+(closable?"notify-msg-pointer":""),_click:function(){closable&&(closeFlag=!0,flag=!1,closeMsg(el,onClose,_msgWrapper))}},getMsgRight(closable))])]);var anm=el.querySelector(".notify-msg__circle");if(anm&&(css(anm,{animation:"notify-msg_"+type+" "+duration+"ms linear"}),"onanimationend"in window?addAnimationEnd(anm,function(){closeMsg(el,onClose,_msgWrapper)}):setTimeout(function(){closeMsg(el,onClose,_msgWrapper)},duration)),"loading"!=type&&setTimeout(function(){closeMsg(el,onClose,_msgWrapper)},duration),_msgWrapper.children.length||document.body.appendChild(_msgWrapper),_msgWrapper.appendChild(el),css(el,{height:el.offsetHeight+"px"}),setTimeout(function(){-1!=positionB.indexOf(position)?removeClass(el.children[0],"notify-msg-fade-in-b"):removeClass(el.children[0],"notify-msg-fade-in")},300),"loading"==type)return function(){closeMsg(el,onClose,_msgWrapper)}}function getMsgRight(showClose){if(showClose)return'\n \n '}function closeMsg(el,cb,_msgWrapper){el&&(css(el,{padding:0,height:0}),$(el.children[0]).hasClass("notify-bottom")?addClass(el.children[0],"notify-msg-fade-out-b"):addClass(el.children[0],"notify-msg-fade-out"),closeFlag?(closeFlag=!1,cb&&cb()):flag?cb&&cb():flag=!0,setTimeout(function(){if(el){var has=!1;if(_msgWrapper){for(var i=0;i<_msgWrapper.children.length;i++)_msgWrapper.children[i]&&_msgWrapper.children[i]===el&&(has=!0);has&&removeChild(el),el=null,_msgWrapper.children.length||has&&removeChild(_msgWrapper)}}},300))}function getIconObj(){return{info:'\n \n ', -success:'\n \n ',warning:'\n \n ',error:'\n \n ',loading:'\n
                  \n \n \n \n
                  \n '}}function removeChild(el){el&&el.parentNode.removeChild(el)}function _destroyAll(){for(var j=0;j',i
                  ':'
                  ');var number=stepItems[i].number;number||(number=i+1),stepDiv+=i==postion?'
                  '+number+"
                  ":i
                  ':'
                  '+number+"
                  ";var title=stepItems[i].title,desc=stepItems[i].desc;(title||desc)&&(stepDiv+='
                  ',title&&(stepDiv+='
                  '+title+"
                  "),desc&&(stepDiv+='
                  '+desc+"
                  "),stepDiv+="
                  "),stepDiv+="
              "}stepDiv+="
              ",$(elem).prepend(stepDiv);var bfb=100/stepItems.length;$(".step-item").css("width",bfb+"%")},step={render:function(param){param.indicator="none",param.arrow="always",param.autoplay=!1,param.stepWidth||(param.stepWidth="400px"),carousel.render(param);var stepItems=param.stepItems;renderDom(param.elem,stepItems,0),$(".lay-step").css("width",param.stepWidth),carousel.on("change("+param.filter+")",function(obj){$(param.elem).find(".lay-step").remove(),renderDom(param.elem,stepItems,obj.index),$(".lay-step").css("width",param.stepWidth)}),$(param.elem).find(".layui-carousel-arrow").css("display","none"),$(param.elem).css("background-color","transparent")},next:function(elem){$(elem).find(".layui-carousel-arrow[lay-type=add]").trigger("click")},pre:function(elem){$(elem).find(".layui-carousel-arrow[lay-type=sub]").trigger("click")}};layui.link("/static/plugins/lay-module/step-lay/step.css"),exports("step",step)}),define("step-lay",function(){});var _createClass=function(){function defineProperties(target,props){for(var i=0;i'+value+""+(this.options.permanentData.includes(value)?"":'×')+""}},{key:"onChange",value:function(value,type){this.options.onChange&&"function"==typeof this.options.onChange&&this.options.onChange(this.copyData,value,type)}},{key:"getData",value:function(){return this.copyData}},{key:"clearData",value:function(){this.options.data=[],this.elem.prevAll("span.layui-tag").remove()}}]),InputTag}();return{render:function(options){return new InputTag(options)}}})}("function"==typeof define&&define.amd?define:function(deps,factory){"undefined"!=typeof module&&module.exports?module.exports=factory(require("jquery")):window.layui&&layui.define?layui.define("jquery",function(exports){exports("inputTag",factory(layui.jquery))}):window.inputTag=factory(window.jQuery)}),layui.link("/static/plugins/lay-module/inputTags/inputTags.css?v=7");var ChineseDistricts={886:{11e4:"北京市",12e4:"天津市",13e4:"河北省",14e4:"山西省",15e4:"内蒙古自治区",21e4:"辽宁省",22e4:"吉林省",23e4:"黑龙江省",31e4:"上海市",32e4:"江苏省",33e4:"浙江省",34e4:"安徽省",35e4:"福建省",36e4:"江西省",37e4:"山东省",41e4:"河南省",42e4:"湖北省",43e4:"湖南省",44e4:"广东省",45e4:"广西壮族自治区",46e4:"海南省",5e5:"重庆市",51e4:"四川省",52e4:"贵州省",53e4:"云南省",54e4:"西藏自治区",61e4:"陕西省",62e4:"甘肃省",63e4:"青海省",64e4:"宁夏回族自治区",65e4:"新疆维吾尔自治区",83e4:"台湾省",81e4:"香港特别行政区",82e4:"澳门特别行政区"},86:{"A-G":[{code:"340000",address:"安徽省"},{code:"110000",address:"北京市"},{code:"500000",address:"重庆市"},{code:"350000",address:"福建省"},{code:"620000",address:"甘肃省"},{code:"440000",address:"广东省"},{code:"450000",address:"广西壮族自治区"},{code:"520000",address:"贵州省"},{code:"820000",address:"澳门特别行政区"}],"H-K":[{code:"460000",address:"海南省"},{code:"130000",address:"河北省"},{code:"230000",address:"黑龙江省"},{code:"410000",address:"河南省"},{code:"420000",address:"湖北省"},{code:"430000",address:"湖南省"},{code:"320000",address:"江苏省"},{code:"360000",address:"江西省"},{code:"220000",address:"吉林省"}],"L-S":[{code:"210000",address:"辽宁省"},{code:"150000",address:"内蒙古自治区"},{code:"640000",address:"宁夏回族自治区"},{code:"630000",address:"青海省"},{code:"370000",address:"山东省"},{code:"310000",address:"上海市"},{code:"140000",address:"山西省"},{code:"610000",address:"陕西省"},{code:"510000",address:"四川省"}],"T-Z":[{code:"120000",address:"天津市"},{code:"830000",address:"台湾省"},{code:"650000",address:"新疆维吾尔自治区"},{code:"540000",address:"西藏自治区"},{code:"810000",address:"香港特别行政区"},{code:"530000",address:"云南省"},{code:"330000",address:"浙江省"}]},11e4:{110100:"北京市"},110100:{110101:"东城区",110102:"西城区",110103:"崇文区",110104:"宣武区",110105:"朝阳区",110106:"丰台区",110107:"石景山区",110108:"海淀区",110109:"门头沟区",110111:"房山区",110112:"通州区",110113:"顺义区",110114:"昌平区",110115:"大兴区",110116:"怀柔区",110117:"平谷区",110228:"密云县",110229:"延庆县"},12e4:{120100:"天津市 "},120100:{120101:"和平区",120102:"河东区",120103:"河西区",120104:"南开区",120105:"河北区",120106:"红桥区",120107:"塘沽区",120108:"汉沽区",120109:"大港区",120110:"东丽区",120111:"西青区",120112:"津南区",120113:"北辰区",120114:"武清区",120115:"宝坻区",120116:"滨海新区",120221:"宁河县",120223:"静海县",120225:"蓟县"},13e4:{130100:"石家庄市",130200:"唐山市",130300:"秦皇岛市",130400:"邯郸市",130500:"邢台市",130600:"保定市",130700:"张家口市",130800:"承德市",130900:"沧州市",131e3:"廊坊市",131100:"衡水市"},130100:{130102:"长安区",130103:"桥东区",130104:"桥西区",130105:"新华区",130107:"井陉矿区",130108:"裕华区",130121:"井陉县",130123:"正定县",130124:"栾城区",130125:"行唐县",130126:"灵寿县",130127:"高邑县",130128:"深泽县",130129:"赞皇县",130130:"无极县",130131:"平山县",130132:"元氏县",130133:"赵县",130181:"辛集市",130182:"藁城区",130183:"晋州市",130184:"新乐市",130185:"鹿泉区"},130200:{130202:"路南区",130203:"路北区",130204:"古冶区",130205:"开平区",130207:"丰南区",130208:"丰润区",130223:"滦县",130224:"滦南县",130225:"乐亭县",130227:"迁西县",130229:"玉田县",130230:"曹妃甸区",130281:"遵化市",130283:"迁安市",130299:"唐海县"},130300:{130302:"海港区",130303:"山海关区",130304:"北戴河区",130321:"青龙满族自治县",130322:"昌黎县",130323:"抚宁县",130324:"卢龙县",130399:"经济技术开发区"},130400:{130402:"邯山区",130403:"丛台区",130404:"复兴区",130406:"峰峰矿区",130421:"邯郸县",130423:"临漳县",130424:"成安县",130425:"大名县",130426:"涉县",130427:"磁县",130428:"肥乡县",130429:"永年县",130430:"邱县",130431:"鸡泽县",130432:"广平县",130433:"馆陶县",130434:"魏县",130435:"曲周县",130481:"武安市"},130500:{130502:"桥东区",130503:"桥西区",130521:"邢台县",130522:"临城县",130523:"内丘县",130524:"柏乡县",130525:"隆尧县",130526:"任县",130527:"南和县",130528:"宁晋县",130529:"巨鹿县",130530:"新河县",130531:"广宗县",130532:"平乡县",130533:"威县",130534:"清河县",130535:"临西县",130581:"南宫市",130582:"沙河市"},130600:{130602:"新市区",130603:"北市区",130604:"南市区",130621:"满城区",130622:"清苑区",130623:"涞水县",130624:"阜平县",130625:"徐水区",130626:"定兴县",130627:"唐县",130628:"高阳县",130629:"容城县",130630:"涞源县",130631:"望都县",130632:"安新县",130633:"易县",130634:"曲阳县",130635:"蠡县",130636:"顺平县",130637:"博野县",130638:"雄县",130681:"涿州市",130682:"定州市",130683:"安国市",130684:"高碑店市",130698:"高开区"},130700:{130702:"桥东区",130703:"桥西区",130705:"宣化区",130706:"下花园区",130721:"宣化县",130722:"张北县",130723:"康保县",130724:"沽源县",130725:"尚义县",130726:"蔚县",130727:"阳原县",130728:"怀安县",130729:"万全县",130730:"怀来县",130731:"涿鹿县",130732:"赤城县",130733:"崇礼县"},130800:{130802:"双桥区",130803:"双滦区",130804:"鹰手营子矿区",130821:"承德县",130822:"兴隆县",130823:"平泉县",130824:"滦平县",130825:"隆化县",130826:"丰宁满族自治县",130827:"宽城满族自治县",130828:"围场满族蒙古族自治县"},130900:{130902:"新华区",130903:"运河区",130921:"沧县",130922:"青县",130923:"东光县",130924:"海兴县",130925:"盐山县",130926:"肃宁县",130927:"南皮县",130928:"吴桥县",130929:"献县",130930:"孟村回族自治县",130981:"泊头市",130982:"任丘市",130983:"黄骅市",130984:"河间市"},131e3:{131002:"安次区",131003:"广阳区",131022:"固安县",131023:"永清县",131024:"香河县",131025:"大城县",131026:"文安县",131028:"大厂回族自治县",131051:"开发区",131052:"燕郊经济技术开发区",131081:"霸州市",131082:"三河市",131099:"廊坊经济技术开发区"},131100:{131102:"桃城区",131121:"枣强县",131122:"武邑县",131123:"武强县",131124:"饶阳县",131125:"安平县",131126:"故城县",131127:"景县",131128:"阜城县",131181:"冀州市",131182:"深州市"},14e4:{140100:"太原市",140200:"大同市",140300:"阳泉市",140400:"长治市",140500:"晋城市",140600:"朔州市",140700:"晋中市",140800:"运城市",140900:"忻州市",141e3:"临汾市",141100:"吕梁市"},140100:{140105:"小店区",140106:"迎泽区",140107:"杏花岭区",140108:"尖草坪区",140109:"万柏林区",140110:"晋源区",140121:"清徐县",140122:"阳曲县",140123:"娄烦县",140181:"古交市"},140200:{140202:"城区",140203:"矿区",140211:"南郊区",140212:"新荣区",140221:"阳高县",140222:"天镇县",140223:"广灵县",140224:"灵丘县",140225:"浑源县",140226:"左云县",140227:"大同县"},140300:{140302:"城区",140303:"矿区",140311:"郊区",140321:"平定县",140322:"盂县"},140400:{140421:"长治县",140423:"襄垣县",140424:"屯留县",140425:"平顺县",140426:"黎城县",140427:"壶关县",140428:"长子县",140429:"武乡县",140430:"沁县",140431:"沁源县",140481:"潞城市",140482:"城区",140483:"郊区",140484:"高新区"},140500:{140502:"城区",140521:"沁水县",140522:"阳城县",140524:"陵川县",140525:"泽州县",140581:"高平市"},140600:{140602:"朔城区",140603:"平鲁区",140621:"山阴县",140622:"应县",140623:"右玉县",140624:"怀仁县"},140700:{140702:"榆次区",140721:"榆社县",140722:"左权县",140723:"和顺县",140724:"昔阳县",140725:"寿阳县",140726:"太谷县",140727:"祁县",140728:"平遥县",140729:"灵石县",140781:"介休市"},140800:{140802:"盐湖区",140803:"风陵渡经济开发区",140821:"临猗县",140822:"万荣县",140823:"闻喜县",140824:"稷山县",140825:"新绛县",140826:"绛县",140827:"垣曲县",140828:"夏县",140829:"平陆县",140830:"芮城县",140881:"永济市",140882:"河津市"},140900:{140902:"忻府区",140921:"定襄县",140922:"五台县",140923:"代县",140924:"繁峙县",140925:"宁武县",140926:"静乐县",140927:"神池县",140928:"五寨县",140929:"岢岚县",140930:"河曲县",140931:"保德县",140932:"偏关县",140981:"原平市"},141e3:{141002:"尧都区",141021:"曲沃县",141022:"翼城县",141023:"襄汾县",141024:"洪洞县",141025:"古县",141026:"安泽县",141027:"浮山县",141028:"吉县",141029:"乡宁县",141030:"大宁县",141031:"隰县",141032:"永和县",141033:"蒲县",141034:"汾西县",141081:"侯马市",141082:"霍州市"},141100:{141102:"离石区",141121:"文水县",141122:"交城县",141123:"兴县",141124:"临县",141125:"柳林县",141126:"石楼县",141127:"岚县",141128:"方山县",141129:"中阳县",141130:"交口县",141181:"孝义市",141182:"汾阳市"},15e4:{150100:"呼和浩特市",150200:"包头市",150300:"乌海市",150400:"赤峰市",150500:"通辽市",150600:"鄂尔多斯市",150700:"呼伦贝尔市",150800:"巴彦淖尔市",150900:"乌兰察布市",152200:"兴安盟",152500:"锡林郭勒盟",152900:"阿拉善盟"},150100:{150102:"新城区",150103:"回民区",150104:"玉泉区",150105:"赛罕区",150121:"土默特左旗",150122:"托克托县",150123:"和林格尔县",150124:"清水河县",150125:"武川县"},150200:{150202:"东河区",150203:"昆都仑区",150204:"青山区",150205:"石拐区",150206:"白云鄂博矿区",150207:"九原区",150221:"土默特右旗",150222:"固阳县",150223:"达尔罕茂明安联合旗"},150300:{150302:"海勃湾区",150303:"海南区",150304:"乌达区"},150400:{150402:"红山区",150403:"元宝山区",150404:"松山区",150421:"阿鲁科尔沁旗",150422:"巴林左旗",150423:"巴林右旗",150424:"林西县",150425:"克什克腾旗",150426:"翁牛特旗",150428:"喀喇沁旗",150429:"宁城县",150430:"敖汉旗"},150500:{150502:"科尔沁区",150521:"科尔沁左翼中旗",150522:"科尔沁左翼后旗",150523:"开鲁县",150524:"库伦旗",150525:"奈曼旗",150526:"扎鲁特旗",150581:"霍林郭勒市"},150600:{150602:"东胜区",150621:"达拉特旗",150622:"准格尔旗",150623:"鄂托克前旗",150624:"鄂托克旗",150625:"杭锦旗",150626:"乌审旗",150627:"伊金霍洛旗"},150700:{150702:"海拉尔区",150703:"扎赉诺尔区",150721:"阿荣旗",150722:"莫力达瓦达斡尔族自治旗",150723:"鄂伦春自治旗",150724:"鄂温克族自治旗",150725:"陈巴尔虎旗",150726:"新巴尔虎左旗",150727:"新巴尔虎右旗",150781:"满洲里市",150782:"牙克石市",150783:"扎兰屯市",150784:"额尔古纳市",150785:"根河市"},150800:{150802:"临河区",150821:"五原县",150822:"磴口县",150823:"乌拉特前旗",150824:"乌拉特中旗",150825:"乌拉特后旗",150826:"杭锦后旗"},150900:{150902:"集宁区",150921:"卓资县",150922:"化德县",150923:"商都县",150924:"兴和县",150925:"凉城县",150926:"察哈尔右翼前旗",150927:"察哈尔右翼中旗",150928:"察哈尔右翼后旗",150929:"四子王旗",150981:"丰镇市"},152200:{152201:"乌兰浩特市",152202:"阿尔山市",152221:"科尔沁右翼前旗",152222:"科尔沁右翼中旗",152223:"扎赉特旗",152224:"突泉县"},152500:{152501:"二连浩特市",152502:"锡林浩特市",152522:"阿巴嘎旗",152523:"苏尼特左旗",152524:"苏尼特右旗",152525:"东乌珠穆沁旗",152526:"西乌珠穆沁旗",152527:"太仆寺旗",152528:"镶黄旗",152529:"正镶白旗",152530:"正蓝旗",152531:"多伦县"},152900:{152921:"阿拉善左旗",152922:"阿拉善右旗",152923:"额济纳旗"},21e4:{210100:"沈阳市",210200:"大连市",210300:"鞍山市",210400:"抚顺市",210500:"本溪市",210600:"丹东市",210700:"锦州市",210800:"营口市",210900:"阜新市",211e3:"辽阳市",211100:"盘锦市",211200:"铁岭市",211300:"朝阳市",211400:"葫芦岛市"},210100:{210102:"和平区",210103:"沈河区",210104:"大东区",210105:"皇姑区",210106:"铁西区",210111:"苏家屯区",210112:"东陵区",210113:"新城子区",210114:"于洪区",210122:"辽中县",210123:"康平县",210124:"法库县",210181:"新民市",210182:"浑南区",210183:"张士开发区",210184:"沈北新区"},210200:{210202:"中山区",210203:"西岗区",210204:"沙河口区",210211:"甘井子区",210212:"旅顺口区",210213:"金州区",210214:"大连经济技术开发区",210224:"长海县",210251:"开发区",210281:"瓦房店市",210282:"普兰店市",210283:"庄河市",210297:"岭前区"},210300:{210302:"铁东区",210303:"铁西区",210304:"立山区",210311:"千山区",210321:"台安县",210323:"岫岩满族自治县",210351:"高新区",210381:"海城市"},210400:{210402:"新抚区",210403:"东洲区",210404:"望花区",210411:"顺城区",210421:"抚顺县",210422:"新宾满族自治县",210423:"清原满族自治县"},210500:{210502:"平山区",210503:"溪湖区",210504:"明山区",210505:"南芬区",210521:"本溪满族自治县",210522:"桓仁满族自治县"},210600:{210602:"元宝区",210603:"振兴区",210604:"振安区",210624:"宽甸满族自治县",210681:"东港市",210682:"凤城市"},210700:{210702:"古塔区",210703:"凌河区",210711:"太和区",210726:"黑山县",210727:"义县",210781:"凌海市",210782:"北镇市"},210800:{210802:"站前区",210803:"西市区",210804:"鲅鱼圈区",210811:"老边区",210881:"盖州市",210882:"大石桥市"},210900:{210902:"海州区",210903:"新邱区",210904:"太平区",210905:"清河门区",210911:"细河区",210921:"阜新蒙古族自治县",210922:"彰武县"},211e3:{211002:"白塔区",211003:"文圣区",211004:"宏伟区",211005:"弓长岭区",211011:"太子河区",211021:"辽阳县",211081:"灯塔市"},211100:{211102:"双台子区",211103:"兴隆台区",211121:"大洼县",211122:"盘山县"},211200:{211202:"银州区",211204:"清河区",211221:"铁岭县",211223:"西丰县",211224:"昌图县",211281:"调兵山市",211282:"开原市"},211300:{211302:"双塔区",211303:"龙城区",211321:"朝阳县",211322:"建平县",211324:"喀喇沁左翼蒙古族自治县",211381:"北票市",211382:"凌源市"},211400:{211402:"连山区",211403:"龙港区",211404:"南票区",211421:"绥中县",211422:"建昌县",211481:"兴城市"},22e4:{220100:"长春市",220200:"吉林市",220300:"四平市",220400:"辽源市",220500:"通化市",220600:"白山市",220700:"松原市",220800:"白城市",222400:"延边朝鲜族自治州"},220100:{220102:"南关区",220103:"宽城区",220104:"朝阳区",220105:"二道区",220106:"绿园区",220112:"双阳区",220117:"高新北区",220119:"高新区",220122:"农安县",220181:"九台区",220182:"榆树市",220183:"德惠市",220184:"高新技术产业开发区",220185:"汽车产业开发区",220186:"经济技术开发区",220187:"净月旅游开发区"},220200:{220202:"昌邑区",220203:"龙潭区",220204:"船营区",220211:"丰满区",220221:"永吉县",220281:"蛟河市",220282:"桦甸市",220283:"舒兰市",220284:"磐石市"},220300:{220302:"铁西区",220303:"铁东区",220322:"梨树县",220323:"伊通满族自治县",220381:"公主岭市",220382:"双辽市"},220400:{220402:"龙山区",220403:"西安区",220421:"东丰县",220422:"东辽县"},220500:{220502:"东昌区",220503:"二道江区",220521:"通化县",220523:"辉南县",220524:"柳河县",220581:"梅河口市",220582:"集安市"},220600:{220602:"浑江区",220603:"八道江区",220621:"抚松县",220622:"靖宇县",220623:"长白朝鲜族自治县",220625:"江源区",220681:"临江市"},220700:{220702:"宁江区",220721:"前郭尔罗斯蒙古族自治县",220722:"长岭县",220723:"乾安县",220781:"扶余市"},220800:{220802:"洮北区",220821:"镇赉县",220822:"通榆县",220881:"洮南市",220882:"大安市"},222400:{222401:"延吉市",222402:"图们市",222403:"敦化市",222404:"珲春市",222405:"龙井市",222406:"和龙市",222424:"汪清县",222426:"安图县"},23e4:{230100:"哈尔滨市",230200:"齐齐哈尔市",230300:"鸡西市",230400:"鹤岗市",230500:"双鸭山市",230600:"大庆市",230700:"伊春市",230800:"佳木斯市",230900:"七台河市",231e3:"牡丹江市",231100:"黑河市",231200:"绥化市",232700:"大兴安岭地区"},230100:{230102:"道里区",230103:"南岗区",230104:"道外区",230106:"香坊区",230107:"动力区",230108:"平房区",230109:"松北区",230110:"香坊区",230111:"呼兰区",230123:"依兰县",230124:"方正县",230125:"宾县",230126:"巴彦县",230127:"木兰县",230128:"通河县",230129:"延寿县",230181:"阿城区",230182:"双城区",230183:"尚志市",230184:"五常市",230185:"阿城市"},230200:{230202:"龙沙区",230203:"建华区",230204:"铁锋区",230205:"昂昂溪区",230206:"富拉尔基区",230207:"碾子山区",230208:"梅里斯达斡尔族区",230221:"龙江县",230223:"依安县",230224:"泰来县",230225:"甘南县",230227:"富裕县",230229:"克山县",230230:"克东县",230231:"拜泉县",230281:"讷河市"},230300:{230302:"鸡冠区",230303:"恒山区",230304:"滴道区",230305:"梨树区",230306:"城子河区",230307:"麻山区",230321:"鸡东县",230381:"虎林市",230382:"密山市"},230400:{230402:"向阳区",230403:"工农区",230404:"南山区",230405:"兴安区",230406:"东山区",230407:"兴山区",230421:"萝北县",230422:"绥滨县"},230500:{230502:"尖山区",230503:"岭东区",230505:"四方台区",230506:"宝山区",230521:"集贤县",230522:"友谊县",230523:"宝清县",230524:"饶河县"},230600:{230602:"萨尔图区",230603:"龙凤区",230604:"让胡路区",230605:"红岗区",230606:"大同区",230621:"肇州县",230622:"肇源县",230623:"林甸县",230624:"杜尔伯特蒙古族自治县"},230700:{230702:"伊春区",230703:"南岔区",230704:"友好区",230705:"西林区",230706:"翠峦区",230707:"新青区",230708:"美溪区",230709:"金山屯区",230710:"五营区",230711:"乌马河区",230712:"汤旺河区",230713:"带岭区",230714:"乌伊岭区",230715:"红星区",230716:"上甘岭区",230722:"嘉荫县",230781:"铁力市"},230800:{230802:"永红区",230803:"向阳区",230804:"前进区",230805:"东风区",230811:"郊区",230822:"桦南县",230826:"桦川县",230828:"汤原县",230833:"抚远县",230881:"同江市",230882:"富锦市"},230900:{230902:"新兴区",230903:"桃山区",230904:"茄子河区",230921:"勃利县"},231e3:{231002:"东安区",231003:"阳明区",231004:"爱民区",231005:"西安区",231024:"东宁县",231025:"林口县",231081:"绥芬河市",231083:"海林市",231084:"宁安市",231085:"穆棱市"},231100:{231102:"爱辉区",231121:"嫩江县",231123:"逊克县",231124:"孙吴县",231181:"北安市",231182:"五大连池市"},231200:{231202:"北林区",231221:"望奎县",231222:"兰西县",231223:"青冈县",231224:"庆安县",231225:"明水县",231226:"绥棱县",231281:"安达市",231282:"肇东市",231283:"海伦市"},232700:{232702:"松岭区",232703:"新林区",232704:"呼中区",232721:"呼玛县",232722:"塔河县",232723:"漠河县",232724:"加格达奇区"},31e4:{310100:"上海市"},310100:{310101:"黄浦区",310103:"卢湾区",310104:"徐汇区",310105:"长宁区",310106:"静安区",310107:"普陀区",310108:"闸北区",310109:"虹口区",310110:"杨浦区",310112:"闵行区",310113:"宝山区",310114:"嘉定区",310115:"浦东新区",310116:"金山区",310117:"松江区",310118:"青浦区",310119:"南汇区",310120:"奉贤区",310152:"川沙区",310230:"崇明县"},32e4:{320100:"南京市",320200:"无锡市",320300:"徐州市",320400:"常州市",320500:"苏州市",320600:"南通市",320700:"连云港市",320800:"淮安市",320900:"盐城市",321e3:"扬州市",321100:"镇江市",321200:"泰州市",321300:"宿迁市"},320100:{320102:"玄武区",320103:"白下区",320104:"秦淮区",320105:"建邺区",320106:"鼓楼区",320107:"下关区",320111:"浦口区",320113:"栖霞区",320114:"雨花台区",320115:"江宁区",320116:"六合区",320124:"溧水区",320125:"高淳区"},320200:{320202:"崇安区",320203:"南长区",320204:"北塘区",320205:"锡山区",320206:"惠山区",320211:"滨湖区",320213:"梁溪区",320214:"新吴区",320281:"江阴市",320282:"宜兴市",320296:"新区"},320300:{320302:"鼓楼区",320303:"云龙区",320304:"九里区",320305:"贾汪区",320311:"泉山区",320321:"丰县",320322:"沛县",320323:"铜山区",320324:"睢宁县",320381:"新沂市",320382:"邳州市"},320400:{320402:"天宁区",320404:"钟楼区",320405:"戚墅堰区",320411:"新北区",320412:"武进区",320481:"溧阳市",320482:"金坛区"},320500:{320502:"沧浪区",320503:"平江区",320504:"金阊区",320505:"虎丘区",320506:"吴中区",320507:"相城区",320508:"姑苏区",320510:"新区",320581:"常熟市",320582:"张家港市",320583:"昆山市",320584:"吴江区",320585:"太仓市",320595:"园区"},320600:{320602:"崇川区",320611:"港闸区",320612:"通州区",320613:"开发区",320621:"海安县",320623:"如东县",320681:"启东市",320682:"如皋市",320683:"通州市",320684:"海门市",320693:"开发区"},320700:{320703:"连云区",320705:"新浦区",320706:"海州区",320721:"赣榆区",320722:"东海县",320723:"灌云县",320724:"灌南县"},320800:{320802:"清河区",320803:"淮安区",320804:"淮阴区",320809:"开发区",320811:"清浦区",320826:"涟水县",320829:"洪泽县",320830:"盱眙县",320831:"金湖县"},320900:{320902:"亭湖区",320903:"盐都区",320921:"响水县",320922:"滨海县",320923:"阜宁县",320924:"射阳县",320925:"建湖县",320981:"东台市",320982:"大丰市"},321e3:{321002:"广陵区",321003:"邗江区",321011:"维扬区",321023:"宝应县",321081:"仪征市",321084:"高邮市",321088:"江都区",321092:"经济开发区"},321100:{321102:"京口区",321111:"润州区",321112:"丹徒区",321181:"丹阳市",321182:"扬中市",321183:"句容市"},321200:{321202:"海陵区",321203:"高港区",321281:"兴化市",321282:"靖江市",321283:"泰兴市",321284:"姜堰区"},321300:{321302:"宿城区",321311:"宿豫区",321322:"沭阳县",321323:"泗阳县",321324:"泗洪县"},33e4:{330100:"杭州市",330200:"宁波市",330300:"温州市",330400:"嘉兴市",330500:"湖州市",330600:"绍兴市",330700:"金华市",330800:"衢州市",330900:"舟山市",331e3:"台州市",331100:"丽水市"},330100:{330102:"上城区",330103:"下城区",330104:"江干区",330105:"拱墅区",330106:"西湖区",330108:"滨江区",330109:"萧山区",330110:"余杭区",330122:"桐庐县",330127:"淳安县",330182:"建德市",330183:"富阳区",330185:"临安市"},330200:{330203:"海曙区",330204:"江东区",330205:"江北区",330206:"北仑区",330211:"镇海区",330212:"鄞州区",330225:"象山县",330226:"宁海县",330281:"余姚市",330282:"慈溪市",330283:"奉化市",330299:"高新区"},330300:{330302:"鹿城区",330303:"龙湾区",330304:"瓯海区",330322:"洞头县",330324:"永嘉县",330326:"平阳县",330327:"苍南县",330328:"文成县",330329:"泰顺县",330381:"瑞安市",330382:"乐清市"},330400:{330402:"南湖区",330411:"秀洲区",330421:"嘉善县",330424:"海盐县",330481:"海宁市",330482:"平湖市",330483:"桐乡市"},330500:{330502:"吴兴区",330503:"南浔区",330521:"德清县",330522:"长兴县",330523:"安吉县"},330600:{330602:"越城区",330621:"柯桥区",330624:"新昌县",330681:"诸暨市",330682:"上虞区",330683:"嵊州市"},330700:{330702:"婺城区",330703:"金东区",330723:"武义县",330726:"浦江县",330727:"磐安县",330781:"兰溪市",330782:"义乌市",330783:"东阳市",330784:"永康市"},330800:{330802:"柯城区",330803:"衢江区",330822:"常山县",330824:"开化县",330825:"龙游县",330881:"江山市"},330900:{330902:"定海区",330903:"普陀区",330921:"岱山县",330922:"嵊泗县"},331e3:{331002:"椒江区",331003:"黄岩区",331004:"路桥区",331021:"玉环县",331022:"三门县",331023:"天台县",331024:"仙居县",331081:"温岭市",331082:"临海市"},331100:{331102:"莲都区",331121:"青田县",331122:"缙云县",331123:"遂昌县",331124:"松阳县",331125:"云和县",331126:"庆元县",331127:"景宁畲族自治县",331181:"龙泉市"},34e4:{340100:"合肥市",340200:"芜湖市",340300:"蚌埠市",340400:"淮南市",340500:"马鞍山市",340600:"淮北市",340700:"铜陵市",340800:"安庆市",341e3:"黄山市",341100:"滁州市",341200:"阜阳市",341300:"宿州市",341500:"六安市",341600:"亳州市",341700:"池州市",341800:"宣城市"},340100:{340102:"瑶海区",340103:"庐阳区",340104:"蜀山区",340111:"包河区",340121:"长丰县",340122:"肥东县",340123:"肥西县",340151:"高新区",340191:"中区",340198:"合肥经济技术开发区",340199:"合肥市政务新区",341400:"巢湖市",341402:"居巢区",341421:"庐江县"},340200:{340202:"镜湖区",340203:"弋江区",340207:"鸠江区",340208:"三山区",340221:"芜湖县",340222:"繁昌县",340223:"南陵县",341422:"无为县"},340300:{340302:"龙子湖区",340303:"蚌山区",340304:"禹会区",340311:"淮上区",340321:"怀远县",340322:"五河县",340323:"固镇县"},340400:{ -340402:"大通区",340403:"田家庵区",340404:"谢家集区",340405:"八公山区",340406:"潘集区",340421:"凤台县",341521:"寿县"},340500:{340502:"金家庄区",340503:"花山区",340504:"雨山区",340506:"博望区",340521:"当涂县",341423:"含山县",341424:"和县"},340600:{340602:"杜集区",340603:"相山区",340604:"烈山区",340621:"濉溪县"},340700:{340702:"铜官山区",340703:"狮子山区",340711:"郊区",340721:"义安区",340823:"枞阳县"},340800:{340802:"迎江区",340803:"大观区",340811:"宜秀区",340822:"怀宁县",340823:"枞阳县",340824:"潜山县",340825:"太湖县",340826:"宿松县",340827:"望江县",340828:"岳西县",340881:"桐城市"},341e3:{341002:"屯溪区",341003:"黄山区",341004:"徽州区",341021:"歙县",341022:"休宁县",341023:"黟县",341024:"祁门县"},341100:{341102:"琅琊区",341103:"南谯区",341122:"来安县",341124:"全椒县",341125:"定远县",341126:"凤阳县",341181:"天长市",341182:"明光市"},341200:{341202:"颍州区",341203:"颍东区",341204:"颍泉区",341221:"临泉县",341222:"太和县",341225:"阜南县",341226:"颍上县",341282:"界首市"},341300:{341302:"埇桥区",341321:"砀山县",341322:"萧县",341323:"灵璧县",341324:"泗县"},341500:{341502:"金安区",341503:"裕安区",341522:"霍邱县",341523:"舒城县",341524:"金寨县",341525:"霍山县",341526:"叶集区"},341600:{341602:"谯城区",341621:"涡阳县",341622:"蒙城县",341623:"利辛县"},341700:{341702:"贵池区",341721:"东至县",341722:"石台县",341723:"青阳县"},341800:{341802:"宣州区",341821:"郎溪县",341822:"广德县",341823:"泾县",341824:"绩溪县",341825:"旌德县",341881:"宁国市"},35e4:{350100:"福州市",350200:"厦门市",350300:"莆田市",350400:"三明市",350500:"泉州市",350600:"漳州市",350700:"南平市",350800:"龙岩市",350900:"宁德市"},350100:{350102:"鼓楼区",350103:"台江区",350104:"仓山区",350105:"马尾区",350111:"晋安区",350121:"闽侯县",350122:"连江县",350123:"罗源县",350124:"闽清县",350125:"永泰县",350128:"平潭县",350181:"福清市",350182:"长乐市"},350200:{350203:"思明区",350205:"海沧区",350206:"湖里区",350211:"集美区",350212:"同安区",350213:"翔安区"},350300:{350302:"城厢区",350303:"涵江区",350304:"荔城区",350305:"秀屿区",350322:"仙游县"},350400:{350402:"梅列区",350403:"三元区",350421:"明溪县",350423:"清流县",350424:"宁化县",350425:"大田县",350426:"尤溪县",350427:"沙县",350428:"将乐县",350429:"泰宁县",350430:"建宁县",350481:"永安市"},350500:{350502:"鲤城区",350503:"丰泽区",350504:"洛江区",350505:"泉港区",350521:"惠安县",350524:"安溪县",350525:"永春县",350526:"德化县",350527:"金门县",350581:"石狮市",350582:"晋江市",350583:"南安市"},350600:{350602:"芗城区",350603:"龙文区",350622:"云霄县",350623:"漳浦县",350624:"诏安县",350625:"长泰县",350626:"东山县",350627:"南靖县",350628:"平和县",350629:"华安县",350681:"龙海市"},350700:{350702:"延平区",350721:"顺昌县",350722:"浦城县",350723:"光泽县",350724:"松溪县",350725:"政和县",350781:"邵武市",350782:"武夷山市",350783:"建瓯市",350784:"建阳区"},350800:{350802:"新罗区",350821:"长汀县",350822:"永定区",350823:"上杭县",350824:"武平县",350825:"连城县",350881:"漳平市"},350900:{350902:"蕉城区",350921:"霞浦县",350922:"古田县",350923:"屏南县",350924:"寿宁县",350925:"周宁县",350926:"柘荣县",350981:"福安市",350982:"福鼎市"},36e4:{360100:"南昌市",360200:"景德镇市",360300:"萍乡市",360400:"九江市",360500:"新余市",360600:"鹰潭市",360700:"赣州市",360800:"吉安市",360900:"宜春市",361e3:"抚州市",361100:"上饶市"},360100:{360102:"东湖区",360103:"西湖区",360104:"青云谱区",360105:"湾里区",360111:"青山湖区",360121:"南昌县",360122:"新建县",360123:"安义县",360124:"进贤县",360125:"红谷滩新区",360126:"经济技术开发区",360127:"昌北区"},360200:{360202:"昌江区",360203:"珠山区",360222:"浮梁县",360281:"乐平市"},360300:{360302:"安源区",360313:"湘东区",360321:"莲花县",360322:"上栗县",360323:"芦溪县"},360400:{360402:"庐山区",360403:"浔阳区",360421:"九江县",360423:"武宁县",360424:"修水县",360425:"永修县",360426:"德安县",360427:"星子县",360428:"都昌县",360429:"湖口县",360430:"彭泽县",360481:"瑞昌市",360483:"共青城市"},360500:{360502:"渝水区",360521:"分宜县"},360600:{360602:"月湖区",360622:"余江县",360681:"贵溪市"},360700:{360702:"章贡区",360704:"赣县",360722:"信丰县",360723:"大余县",360724:"上犹县",360725:"崇义县",360726:"安远县",360727:"龙南县",360728:"定南县",360729:"全南县",360730:"宁都县",360731:"于都县",360732:"兴国县",360733:"会昌县",360734:"寻乌县",360735:"石城县",360751:"黄金区",360781:"瑞金市",360782:"南康区"},360800:{360802:"吉州区",360803:"青原区",360821:"吉安县",360822:"吉水县",360823:"峡江县",360824:"新干县",360825:"永丰县",360826:"泰和县",360827:"遂川县",360828:"万安县",360829:"安福县",360830:"永新县",360881:"井冈山市"},360900:{360902:"袁州区",360921:"奉新县",360922:"万载县",360923:"上高县",360924:"宜丰县",360925:"靖安县",360926:"铜鼓县",360981:"丰城市",360982:"樟树市",360983:"高安市"},361e3:{361002:"临川区",361021:"南城县",361022:"黎川县",361023:"南丰县",361024:"崇仁县",361025:"乐安县",361026:"宜黄县",361027:"金溪县",361028:"资溪县",361029:"东乡县",361030:"广昌县"},361100:{361102:"信州区",361121:"上饶县",361122:"广丰县",361123:"玉山县",361124:"铅山县",361125:"横峰县",361126:"弋阳县",361127:"余干县",361128:"鄱阳县",361129:"万年县",361130:"婺源县",361181:"德兴市"},37e4:{370100:"济南市",370200:"青岛市",370300:"淄博市",370400:"枣庄市",370500:"东营市",370600:"烟台市",370700:"潍坊市",370800:"济宁市",370900:"泰安市",371e3:"威海市",371100:"日照市",371200:"莱芜市",371300:"临沂市",371400:"德州市",371500:"聊城市",371600:"滨州市",371700:"菏泽市"},370100:{250101:"高新区",370102:"历下区",370103:"市中区",370104:"槐荫区",370105:"天桥区",370112:"历城区",370113:"长清区",370124:"平阴县",370125:"济阳县",370126:"商河县",370181:"章丘市"},370200:{370202:"市南区",370203:"市北区",370204:"西海岸新区",370205:"四方区",370211:"黄岛区",370212:"崂山区",370213:"李沧区",370214:"城阳区",370251:"开发区",370281:"胶州市",370282:"即墨市",370283:"平度市",370284:"胶南市",370285:"莱西市"},370300:{370302:"淄川区",370303:"张店区",370304:"博山区",370305:"临淄区",370306:"周村区",370321:"桓台县",370322:"高青县",370323:"沂源县"},370400:{370402:"市中区",370403:"薛城区",370404:"峄城区",370405:"台儿庄区",370406:"山亭区",370481:"滕州市"},370500:{370502:"东营区",370503:"河口区",370521:"垦利县",370522:"利津县",370523:"广饶县",370589:"西城区",370590:"东城区"},370600:{370602:"芝罘区",370611:"福山区",370612:"牟平区",370613:"莱山区",370634:"长岛县",370681:"龙口市",370682:"莱阳市",370683:"莱州市",370684:"蓬莱市",370685:"招远市",370686:"栖霞市",370687:"海阳市"},370700:{370702:"潍城区",370703:"寒亭区",370704:"坊子区",370705:"奎文区",370706:"滨海经济技术开发区",370724:"临朐县",370725:"昌乐县",370751:"开发区",370781:"青州市",370782:"诸城市",370783:"寿光市",370784:"安丘市",370785:"高密市",370786:"昌邑市"},370800:{370802:"市中区",370811:"任城区",370826:"微山县",370827:"鱼台县",370828:"金乡县",370829:"嘉祥县",370830:"汶上县",370831:"泗水县",370832:"梁山县",370881:"曲阜市",370882:"兖州区",370883:"邹城市"},370900:{370902:"泰山区",370903:"岱岳区",370921:"宁阳县",370923:"东平县",370982:"新泰市",370983:"肥城市"},371e3:{371002:"环翠区",371081:"文登区",371082:"荣成市",371083:"乳山市"},371100:{371102:"东港区",371103:"岚山区",371121:"五莲县",371122:"莒县"},371200:{371202:"莱城区",371203:"钢城区"},371300:{371302:"兰山区",371311:"罗庄区",371312:"河东区",371321:"沂南县",371322:"郯城县",371323:"沂水县",371324:"兰陵县",371325:"费县",371326:"平邑县",371327:"莒南县",371328:"蒙阴县",371329:"临沭县"},371400:{371402:"德城区",371421:"陵城区",371422:"宁津县",371423:"庆云县",371424:"临邑县",371425:"齐河县",371426:"平原县",371427:"夏津县",371428:"武城县",371451:"开发区",371481:"乐陵市",371482:"禹城市"},371500:{371502:"东昌府区",371521:"阳谷县",371522:"莘县",371523:"茌平县",371524:"东阿县",371525:"冠县",371526:"高唐县",371581:"临清市"},371600:{371602:"滨城区",371621:"惠民县",371622:"阳信县",371623:"无棣县",371624:"沾化区",371625:"博兴县",371626:"邹平县"},371700:{371702:"牡丹区",371721:"曹县",371722:"单县",371723:"成武县",371724:"巨野县",371725:"郓城县",371726:"鄄城县",371727:"定陶县",371728:"东明县"},41e4:{410100:"郑州市",410200:"开封市",410300:"洛阳市",410400:"平顶山市",410500:"安阳市",410600:"鹤壁市",410700:"新乡市",410800:"焦作市",410900:"濮阳市",411e3:"许昌市",411100:"漯河市",411200:"三门峡市",411300:"南阳市",411400:"商丘市",411500:"信阳市",411600:"周口市",411700:"驻马店市",419001:"济源市"},410100:{410102:"中原区",410103:"二七区",410104:"管城回族区",410105:"金水区",410106:"上街区",410108:"惠济区",410122:"中牟县",410181:"巩义市",410182:"荥阳市",410183:"新密市",410184:"新郑市",410185:"登封市",410186:"郑东新区",410187:"高新区",410193:"经济开发区"},410200:{410202:"龙亭区",410203:"顺河回族区",410204:"鼓楼区",410205:"禹王台区",410211:"金明区",410221:"杞县",410222:"通许县",410223:"尉氏县",410224:"祥符区",410225:"兰考县"},410300:{410302:"老城区",410303:"西工区",410304:"瀍河回族区",410305:"涧西区",410306:"吉利区",410307:"洛龙区",410322:"孟津县",410323:"新安县",410324:"栾川县",410325:"嵩县",410326:"汝阳县",410327:"宜阳县",410328:"洛宁县",410329:"伊川县",410381:"偃师市"},410400:{410402:"新华区",410403:"卫东区",410404:"石龙区",410411:"湛河区",410421:"宝丰县",410422:"叶县",410423:"鲁山县",410425:"郏县",410481:"舞钢市",410482:"汝州市"},410500:{410502:"文峰区",410503:"北关区",410505:"殷都区",410506:"龙安区",410522:"安阳县",410523:"汤阴县",410526:"滑县",410527:"内黄县",410581:"林州市"},410600:{410602:"鹤山区",410603:"山城区",410611:"淇滨区",410621:"浚县",410622:"淇县"},410700:{410702:"红旗区",410703:"卫滨区",410704:"凤泉区",410711:"牧野区",410721:"新乡县",410724:"获嘉县",410725:"原阳县",410726:"延津县",410727:"封丘县",410728:"长垣县",410781:"卫辉市",410782:"辉县市"},410800:{410802:"解放区",410803:"中站区",410804:"马村区",410811:"山阳区",410821:"修武县",410822:"博爱县",410823:"武陟县",410825:"温县",410882:"沁阳市",410883:"孟州市"},410900:{410902:"华龙区",410922:"清丰县",410923:"南乐县",410926:"范县",410927:"台前县",410928:"濮阳县"},411e3:{411002:"魏都区",411023:"许昌县",411024:"鄢陵县",411025:"襄城县",411081:"禹州市",411082:"长葛市"},411100:{411102:"源汇区",411103:"郾城区",411104:"召陵区",411121:"舞阳县",411122:"临颍县"},411200:{411202:"湖滨区",411221:"渑池县",411222:"陕州区",411224:"卢氏县",411281:"义马市",411282:"灵宝市"},411300:{411302:"宛城区",411303:"卧龙区",411321:"南召县",411322:"方城县",411323:"西峡县",411324:"镇平县",411325:"内乡县",411326:"淅川县",411327:"社旗县",411328:"唐河县",411329:"新野县",411330:"桐柏县",411381:"邓州市"},411400:{411402:"梁园区",411403:"睢阳区",411421:"民权县",411422:"睢县",411423:"宁陵县",411424:"柘城县",411425:"虞城县",411426:"夏邑县",411481:"永城市"},411500:{411502:"浉河区",411503:"平桥区",411521:"罗山县",411522:"光山县",411523:"新县",411524:"商城县",411525:"固始县",411526:"潢川县",411527:"淮滨县",411528:"息县"},411600:{411602:"川汇区",411621:"扶沟县",411622:"西华县",411623:"商水县",411624:"沈丘县",411625:"郸城县",411626:"淮阳县",411627:"太康县",411628:"鹿邑县",411681:"项城市"},411700:{411702:"驿城区",411721:"西平县",411722:"上蔡县",411723:"平舆县",411724:"正阳县",411725:"确山县",411726:"泌阳县",411727:"汝南县",411728:"遂平县",411729:"新蔡县"},42e4:{420100:"武汉市",420200:"黄石市",420300:"十堰市",420500:"宜昌市",420600:"襄阳市",420700:"鄂州市",420800:"荆门市",420900:"孝感市",421e3:"荆州市",421100:"黄冈市",421200:"咸宁市",421300:"随州市",422800:"恩施土家族苗族自治州",422900:"仙桃市",429005:"潜江市",429006:"天门市",429021:"神农架林区"},420100:{420102:"江岸区",420103:"江汉区",420104:"硚口区",420105:"汉阳区",420106:"武昌区",420107:"青山区",420111:"洪山区",420112:"东西湖区",420113:"汉南区",420114:"蔡甸区",420115:"江夏区",420116:"黄陂区",420117:"新洲区",420199:"武汉经济技术开发区"},420200:{420202:"黄石港区",420203:"西塞山区",420204:"下陆区",420205:"铁山区",420222:"阳新县",420281:"大冶市"},420300:{420302:"茅箭区",420303:"张湾区",420321:"郧阳区",420322:"郧西县",420323:"竹山县",420324:"竹溪县",420325:"房县",420381:"丹江口市",420382:"城区"},420500:{420502:"西陵区",420503:"伍家岗区",420504:"点军区",420505:"猇亭区",420506:"夷陵区",420525:"远安县",420526:"兴山县",420527:"秭归县",420528:"长阳土家族自治县",420529:"五峰土家族自治县",420551:"葛洲坝区",420552:"开发区",420581:"宜都市",420582:"当阳市",420583:"枝江市"},420600:{420602:"襄城区",420606:"樊城区",420607:"襄州区",420624:"南漳县",420625:"谷城县",420626:"保康县",420682:"老河口市",420683:"枣阳市",420684:"宜城市"},420700:{420702:"梁子湖区",420703:"华容区",420704:"鄂城区"},420800:{420802:"东宝区",420804:"掇刀区",420821:"京山县",420822:"沙洋县",420881:"钟祥市"},420900:{420902:"孝南区",420921:"孝昌县",420922:"大悟县",420923:"云梦县",420981:"应城市",420982:"安陆市",420984:"汉川市"},421e3:{421002:"沙市区",421003:"荆州区",421022:"公安县",421023:"监利县",421024:"江陵县",421081:"石首市",421083:"洪湖市",421087:"松滋市"},421100:{421102:"黄州区",421121:"团风县",421122:"红安县",421123:"罗田县",421124:"英山县",421125:"浠水县",421126:"蕲春县",421127:"黄梅县",421181:"麻城市",421182:"武穴市"},421200:{421202:"咸安区",421221:"嘉鱼县",421222:"通城县",421223:"崇阳县",421224:"通山县",421281:"赤壁市",421282:"温泉城区"},421300:{421303:"曾都区",421321:"随县",421381:"广水市"},422800:{422801:"恩施市",422802:"利川市",422822:"建始县",422823:"巴东县",422825:"宣恩县",422826:"咸丰县",422827:"来凤县",422828:"鹤峰县"},43e4:{430100:"长沙市",430200:"株洲市",430300:"湘潭市",430400:"衡阳市",430500:"邵阳市",430600:"岳阳市",430700:"常德市",430800:"张家界市",430900:"益阳市",431e3:"郴州市",431100:"永州市",431200:"怀化市",431300:"娄底市",433100:"湘西土家族苗族自治州"},430100:{430102:"芙蓉区",430103:"天心区",430104:"岳麓区",430105:"开福区",430111:"雨花区",430121:"长沙县",430122:"望城区",430124:"宁乡县",430181:"浏阳市"},430200:{430202:"荷塘区",430203:"芦淞区",430204:"石峰区",430211:"天元区",430221:"株洲县",430223:"攸县",430224:"茶陵县",430225:"炎陵县",430281:"醴陵市"},430300:{430302:"雨湖区",430304:"岳塘区",430321:"湘潭县",430381:"湘乡市",430382:"韶山市"},430400:{430405:"珠晖区",430406:"雁峰区",430407:"石鼓区",430408:"蒸湘区",430412:"南岳区",430421:"衡阳县",430422:"衡南县",430423:"衡山县",430424:"衡东县",430426:"祁东县",430481:"耒阳市",430482:"常宁市"},430500:{430502:"双清区",430503:"大祥区",430511:"北塔区",430521:"邵东县",430522:"新邵县",430523:"邵阳县",430524:"隆回县",430525:"洞口县",430527:"绥宁县",430528:"新宁县",430529:"城步苗族自治县",430581:"武冈市"},430600:{430602:"岳阳楼区",430603:"云溪区",430611:"君山区",430621:"岳阳县",430623:"华容县",430624:"湘阴县",430626:"平江县",430681:"汨罗市",430682:"临湘市"},430700:{430702:"武陵区",430703:"鼎城区",430721:"安乡县",430722:"汉寿县",430723:"澧县",430724:"临澧县",430725:"桃源县",430726:"石门县",430781:"津市市"},430800:{430802:"永定区",430811:"武陵源区",430821:"慈利县",430822:"桑植县"},430900:{430902:"资阳区",430903:"赫山区",430921:"南县",430922:"桃江县",430923:"安化县",430981:"沅江市"},431e3:{431002:"北湖区",431003:"苏仙区",431021:"桂阳县",431022:"宜章县",431023:"永兴县",431024:"嘉禾县",431025:"临武县",431026:"汝城县",431027:"桂东县",431028:"安仁县",431081:"资兴市"},431100:{431102:"零陵区",431103:"冷水滩区",431121:"祁阳县",431122:"东安县",431123:"双牌县",431124:"道县",431125:"江永县",431126:"宁远县",431127:"蓝山县",431128:"新田县",431129:"江华瑶族自治县"},431200:{431202:"鹤城区",431221:"中方县",431222:"沅陵县",431223:"辰溪县",431224:"溆浦县",431225:"会同县",431226:"麻阳苗族自治县",431227:"新晃侗族自治县",431228:"芷江侗族自治县",431229:"靖州苗族侗族自治县",431230:"通道侗族自治县",431281:"洪江市"},431300:{431302:"娄星区",431321:"双峰县",431322:"新化县",431381:"冷水江市",431382:"涟源市"},433100:{433101:"吉首市",433122:"泸溪县",433123:"凤凰县",433124:"花垣县",433125:"保靖县",433126:"古丈县",433127:"永顺县",433130:"龙山县"},44e4:{440100:"广州市",440200:"韶关市",440300:"深圳市",440400:"珠海市",440500:"汕头市",440600:"佛山市",440700:"江门市",440800:"湛江市",440900:"茂名市",441200:"肇庆市",441300:"惠州市",441400:"梅州市",441500:"汕尾市",441600:"河源市",441700:"阳江市",441800:"清远市",441900:"东莞市",442e3:"中山市",442101:"东沙群岛",445100:"潮州市",445200:"揭阳市",445300:"云浮市"},440100:{440103:"荔湾区",440104:"越秀区",440105:"海珠区",440106:"天河区",440111:"白云区",440112:"黄埔区",440113:"番禺区",440114:"花都区",440115:"南沙区",440116:"萝岗区",440118:"增城区",440184:"从化区",440188:"东山区"},440200:{440203:"武江区",440204:"浈江区",440205:"曲江区",440222:"始兴县",440224:"仁化县",440229:"翁源县",440232:"乳源瑶族自治县",440233:"新丰县",440281:"乐昌市",440282:"南雄市"},440300:{440303:"罗湖区",440304:"福田区",440305:"南山区",440306:"宝安区",440307:"龙岗区",440308:"盐田区",440320:"光明新区",440321:"坪山新区",440322:"大鹏新区",440323:"龙华新区"},440400:{440402:"香洲区",440403:"斗门区",440404:"金湾区",440486:"金唐区",440487:"南湾区"},440500:{440507:"龙湖区",440511:"金平区",440512:"濠江区",440513:"潮阳区",440514:"潮南区",440515:"澄海区",440523:"南澳县"},440600:{440604:"禅城区",440605:"南海区",440606:"顺德区",440607:"三水区",440608:"高明区"},440700:{440703:"蓬江区",440704:"江海区",440705:"新会区",440781:"台山市",440783:"开平市",440784:"鹤山市",440785:"恩平市"},440800:{440802:"赤坎区",440803:"霞山区",440804:"坡头区",440811:"麻章区",440823:"遂溪县",440825:"徐闻县",440881:"廉江市",440882:"雷州市",440883:"吴川市"},440900:{440902:"茂南区",440903:"茂港区",440923:"电白区",440981:"高州市",440982:"化州市",440983:"信宜市"},441200:{441202:"端州区",441203:"鼎湖区",441223:"广宁县",441224:"怀集县",441225:"封开县",441226:"德庆县",441283:"高要区",441284:"四会市"},441300:{441302:"惠城区",441303:"惠阳区",441322:"博罗县",441323:"惠东县",441324:"龙门县",441325:"大亚湾区"},441400:{441402:"梅江区",441421:"梅县",441422:"大埔县",441423:"丰顺县",441424:"五华县",441426:"平远县",441427:"蕉岭县",441481:"兴宁市"},441500:{441502:"城区",441521:"海丰县",441523:"陆河县",441581:"陆丰市"},441600:{441602:"源城区",441621:"紫金县",441622:"龙川县",441623:"连平县",441624:"和平县",441625:"东源县"},441700:{441702:"江城区",441721:"阳西县",441723:"阳东区",441781:"阳春市"},441800:{441802:"清城区",441821:"佛冈县",441823:"阳山县",441825:"连山壮族瑶族自治县",441826:"连南瑶族自治县",441827:"清新区",441881:"英德市",441882:"连州市"},442e3:{442002:"中山火炬开发区",442003:"石岐区"},445100:{445102:"湘桥区",445121:"潮安区",445122:"饶平县",445185:"枫溪区"},445200:{445202:"榕城区",445221:"揭东区",445222:"揭西县",445224:"惠来县",445281:"普宁市",445284:"东山区"},445300:{445302:"云城区",445321:"新兴县",445322:"郁南县",445323:"云安区",445381:"罗定市"},45e4:{450100:"南宁市",450200:"柳州市",450300:"桂林市",450400:"梧州市",450500:"北海市",450600:"防城港市",450700:"钦州市",450800:"贵港市",450900:"玉林市",451e3:"百色市",451100:"贺州市",451200:"河池市",451300:"来宾市",451400:"崇左市"},450100:{450102:"兴宁区",450103:"青秀区",450105:"江南区",450107:"西乡塘区",450108:"良庆区",450109:"邕宁区",450110:"武鸣区",450123:"隆安县",450124:"马山县",450125:"上林县",450126:"宾阳县",450127:"横县"},450200:{450202:"城中区",450203:"鱼峰区",450204:"柳南区",450205:"柳北区",450221:"柳江县",450222:"柳城县",450223:"鹿寨县",450224:"融安县",450225:"融水苗族自治县",450226:"三江侗族自治县"},450300:{450302:"秀峰区",450303:"叠彩区",450304:"象山区",450305:"七星区",450311:"雁山区",450321:"阳朔县",450322:"临桂区",450323:"灵川县",450324:"全州县",450325:"兴安县",450326:"永福县",450327:"灌阳县",450328:"龙胜各族自治县",450329:"资源县",450330:"平乐县",450331:"荔浦县",450332:"恭城瑶族自治县"},450400:{450403:"万秀区",450404:"蝶山区",450405:"长洲区",450406:"龙圩区",450421:"苍梧县",450422:"藤县",450423:"蒙山县",450481:"岑溪市"},450500:{450502:"海城区",450503:"银海区",450512:"铁山港区",450521:"合浦县"},450600:{450602:"港口区",450603:"防城区",450621:"上思县",450681:"东兴市"},450700:{450702:"钦南区",450703:"钦北区",450721:"灵山县",450722:"浦北县"},450800:{450802:"港北区",450803:"港南区",450804:"覃塘区",450821:"平南县",450881:"桂平市"},450900:{450902:"玉州区",450903:"福绵区",450921:"容县",450922:"陆川县",450923:"博白县",450924:"兴业县",450981:"北流市"},451e3:{451002:"右江区",451021:"田阳县",451022:"田东县",451023:"平果县",451024:"德保县",451025:"靖西县",451026:"那坡县",451027:"凌云县",451028:"乐业县",451029:"田林县",451030:"西林县",451031:"隆林各族自治县"},451100:{451102:"八步区",451119:"平桂管理区",451121:"昭平县",451122:"钟山县",451123:"富川瑶族自治县"},451200:{451202:"金城江区",451221:"南丹县",451222:"天峨县",451223:"凤山县",451224:"东兰县",451225:"罗城仫佬族自治县",451226:"环江毛南族自治县",451227:"巴马瑶族自治县",451228:"都安瑶族自治县",451229:"大化瑶族自治县",451281:"宜州市"},451300:{451302:"兴宾区",451321:"忻城县",451322:"象州县",451323:"武宣县",451324:"金秀瑶族自治县",451381:"合山市"},451400:{451402:"江州区",451421:"扶绥县",451422:"宁明县",451423:"龙州县",451424:"大新县",451425:"天等县",451481:"凭祥市"},46e4:{460100:"海口市",460200:"三亚市",460300:"三沙市",469001:"五指山市",469002:"琼海市",469003:"儋州市",469005:"文昌市",469006:"万宁市",469007:"东方市",469025:"定安县",469026:"屯昌县",469027:"澄迈县",469028:"临高县",469031:"昌江黎族自治县",469033:"乐东黎族自治县",469034:"陵水黎族自治县",469035:"保亭黎族苗族自治县",469036:"琼中黎族苗族自治县"},460100:{460105:"秀英区",460106:"龙华区",460107:"琼山区",460108:"美兰区"},460200:{460201:"三亚市"},460300:{460321:"西沙群岛",460322:"南沙群岛",460323:"中沙群岛的岛礁及其海域"},5e5:{500100:"重庆市"},500100:{500101:"万州区",500102:"涪陵区",500103:"渝中区",500104:"大渡口区",500105:"江北区",500106:"沙坪坝区",500107:"九龙坡区",500108:"南岸区",500109:"北碚区",500110:"万盛区",500111:"双桥区",500112:"渝北区",500113:"巴南区",500114:"黔江区",500115:"长寿区",500199:"北部新区",500200:"两江新区",500222:"綦江区",500223:"潼南区",500224:"铜梁县",500225:"大足区",500226:"荣昌区",500227:"璧山区",500228:"梁平县",500229:"城口县",500230:"丰都县",500231:"垫江县",500232:"武隆县",500233:"忠县",500234:"开县",500235:"云阳县",500236:"奉节县",500237:"巫山县",500238:"巫溪县",500240:"石柱土家族自治县",500241:"秀山土家族苗族自治县",500242:"酉阳土家族苗族自治县",500243:"彭水苗族土家族自治县",500381:"江津区",500382:"合川区",500383:"永川区",500384:"南川区"},51e4:{510100:"成都市",510300:"自贡市",510400:"攀枝花市",510500:"泸州市",510600:"德阳市",510700:"绵阳市",510800:"广元市",510900:"遂宁市",511e3:"内江市",511100:"乐山市",511300:"南充市",511400:"眉山市",511500:"宜宾市",511600:"广安市",511700:"达州市",511800:"雅安市",511900:"巴中市",512e3:"资阳市",513200:"阿坝藏族羌族自治州",513300:"甘孜藏族自治州",513400:"凉山彝族自治州"},510100:{510104:"锦江区",510105:"青羊区",510106:"金牛区",510107:"武侯区",510108:"成华区",510112:"龙泉驿区",510113:"青白江区",510114:"新都区",510115:"温江区",510116:"天府新区",510117:"高新区",510121:"金堂县",510122:"双流县",510124:"郫县",510129:"大邑县",510131:"蒲江县",510132:"新津县",510181:"都江堰市",510182:"彭州市",510183:"邛崃市",510184:"崇州市"},510300:{510302:"自流井区",510303:"贡井区",510304:"大安区",510311:"沿滩区",510321:"荣县",510322:"富顺县"},510400:{510402:"东区",510403:"西区",510411:"仁和区",510421:"米易县",510422:"盐边县"},510500:{510502:"江阳区",510503:"纳溪区",510504:"龙马潭区",510521:"泸县",510522:"合江县",510524:"叙永县",510525:"古蔺县"},510600:{510603:"旌阳区",510623:"中江县",510626:"罗江县",510681:"广汉市",510682:"什邡市",510683:"绵竹市"},510700:{510703:"涪城区",510704:"游仙区",510722:"三台县",510723:"盐亭县",510724:"安县",510725:"梓潼县",510726:"北川羌族自治县",510727:"平武县",510751:"高新区",510781:"江油市"},510800:{510802:"利州区",510811:"昭化区",510812:"朝天区",510821:"旺苍县",510822:"青川县",510823:"剑阁县",510824:"苍溪县"},510900:{510903:"船山区",510904:"安居区",510921:"蓬溪县",510922:"射洪县",510923:"大英县"},511e3:{511002:"市中区",511011:"东兴区",511024:"威远县",511025:"资中县",511028:"隆昌县"},511100:{511102:"市中区",511111:"沙湾区",511112:"五通桥区",511113:"金口河区",511123:"犍为县",511124:"井研县",511126:"夹江县",511129:"沐川县",511132:"峨边彝族自治县",511133:"马边彝族自治县",511181:"峨眉山市"},511300:{511302:"顺庆区",511303:"高坪区",511304:"嘉陵区",511321:"南部县",511322:"营山县",511323:"蓬安县",511324:"仪陇县",511325:"西充县",511381:"阆中市"},511400:{511402:"东坡区",511421:"仁寿县",511422:"彭山区",511423:"洪雅县",511424:"丹棱县",511425:"青神县"},511500:{511502:"翠屏区",511521:"宜宾县",511522:"南溪区",511523:"江安县",511524:"长宁县",511525:"高县",511526:"珙县",511527:"筠连县",511528:"兴文县",511529:"屏山县"},511600:{511602:"广安区",511603:"前锋区",511621:"岳池县",511622:"武胜县",511623:"邻水县",511681:"华蓥市",511682:"市辖区"},511700:{511702:"通川区",511721:"达川区",511722:"宣汉县",511723:"开江县",511724:"大竹县",511725:"渠县",511781:"万源市"},511800:{511802:"雨城区",511821:"名山区",511822:"荥经县",511823:"汉源县",511824:"石棉县",511825:"天全县",511826:"芦山县",511827:"宝兴县"},511900:{511902:"巴州区",511903:"恩阳区",511921:"通江县",511922:"南江县",511923:"平昌县"},512e3:{512002:"雁江区",512021:"安岳县",512022:"乐至县",512081:"简阳市"},513200:{513221:"汶川县",513222:"理县",513223:"茂县",513224:"松潘县",513225:"九寨沟县",513226:"金川县",513227:"小金县",513228:"黑水县",513229:"马尔康县",513230:"壤塘县",513231:"阿坝县",513232:"若尔盖县",513233:"红原县"},513300:{513321:"康定区",513322:"泸定县",513323:"丹巴县",513324:"九龙县",513325:"雅江县",513326:"道孚县",513327:"炉霍县",513328:"甘孜县",513329:"新龙县",513330:"德格县",513331:"白玉县",513332:"石渠县",513333:"色达县",513334:"理塘县",513335:"巴塘县",513336:"乡城县",513337:"稻城县",513338:"得荣县"},513400:{513401:"西昌市",513422:"木里藏族自治县",513423:"盐源县",513424:"德昌县",513425:"会理县",513426:"会东县",513427:"宁南县",513428:"普格县",513429:"布拖县",513430:"金阳县",513431:"昭觉县",513432:"喜德县",513433:"冕宁县",513434:"越西县",513435:"甘洛县",513436:"美姑县",513437:"雷波县"},52e4:{520100:"贵阳市",520200:"六盘水市",520300:"遵义市",520400:"安顺市",522200:"铜仁市",522300:"黔西南布依族苗族自治州",522400:"毕节市",522600:"黔东南苗族侗族自治州",522700:"黔南布依族苗族自治州"},520100:{520102:"铜仁市",520103:"云岩区",520111:"花溪区",520112:"乌当区",520113:"白云区",520114:"小河区",520121:"开阳县",520122:"息烽县",520123:"修文县",520151:"观山湖区",520181:"清镇市"},520200:{520201:"钟山区",520203:"六枝特区",520221:"水城县",520222:"盘县"},520300:{520302:"红花岗区",520303:"汇川区",520321:"遵义县",520322:"桐梓县",520323:"绥阳县",520324:"正安县",520325:"道真仡佬族苗族自治县",520326:"务川仡佬族苗族自治县",520327:"凤冈县",520328:"湄潭县",520329:"余庆县",520330:"习水县",520381:"赤水市",520382:"仁怀市"},520400:{520402:"西秀区",520421:"平坝区",520422:"普定县",520423:"镇宁布依族苗族自治县",520424:"关岭布依族苗族自治县",520425:"紫云苗族布依族自治县"},522200:{522201:"碧江区",522222:"江口县",522223:"玉屏侗族自治县",522224:"石阡县",522225:"思南县",522226:"印江土家族苗族自治县",522227:"德江县",522228:"沿河土家族自治县",522229:"松桃苗族自治县",522230:"万山区"},522300:{522301:"兴义市",522322:"兴仁县",522323:"普安县",522324:"晴隆县",522325:"贞丰县",522326:"望谟县",522327:"册亨县",522328:"安龙县"},522400:{520502:"七星关区",520521:"大方县",520522:"黔西县",520523:"金沙县",520524:"织金县",520525:"纳雍县",520526:"威宁彝族回族苗族自治县",520527:"赫章县"},522600:{522601:"凯里市",522622:"黄平县",522623:"施秉县",522624:"三穗县",522625:"镇远县",522626:"岑巩县",522627:"天柱县",522628:"锦屏县",522629:"剑河县",522630:"台江县",522631:"黎平县",522632:"榕江县",522633:"从江县",522634:"雷山县",522635:"麻江县",522636:"丹寨县"},522700:{522701:"都匀市",522702:"福泉市",522722:"荔波县",522723:"贵定县",522725:"瓮安县",522726:"独山县",522727:"平塘县",522728:"罗甸县",522729:"长顺县",522730:"龙里县",522731:"惠水县",522732:"三都水族自治县"},53e4:{530100:"昆明市",530300:"曲靖市",530400:"玉溪市",530500:"保山市",530600:"昭通市",530700:"丽江市",530800:"普洱市",530900:"临沧市",532300:"楚雄彝族自治州",532500:"红河哈尼族彝族自治州",532600:"文山壮族苗族自治州",532800:"西双版纳傣族自治州",532900:"大理白族自治州",533100:"德宏傣族景颇族自治州",533300:"怒江傈僳族自治州",533400:"迪庆藏族自治州"},530100:{530102:"五华区",530103:"盘龙区",530111:"官渡区",530112:"西山区",530113:"东川区",530114:"呈贡区",530122:"晋宁县",530124:"富民县",530125:"宜良县",530126:"石林彝族自治县",530127:"嵩明县",530128:"禄劝彝族苗族自治县",530129:"寻甸回族彝族自治县",530181:"安宁市"},530300:{530302:"麒麟区",530321:"马龙县",530322:"陆良县",530323:"师宗县",530324:"罗平县",530325:"富源县",530326:"会泽县",530328:"沾益县",530381:"宣威市"},530400:{530402:"红塔区",530421:"江川县",530422:"澄江县",530423:"通海县",530424:"华宁县",530425:"易门县",530426:"峨山彝族自治县",530427:"新平彝族傣族自治县",530428:"元江哈尼族彝族傣族自治县"},530500:{530502:"隆阳区",530521:"施甸县",530522:"腾冲县",530523:"龙陵县",530524:"昌宁县"},530600:{530602:"昭阳区",530621:"鲁甸县",530622:"巧家县",530623:"盐津县",530624:"大关县",530625:"永善县",530626:"绥江县",530627:"镇雄县",530628:"彝良县",530629:"威信县",530630:"水富县"},530700:{530702:"古城区",530721:"玉龙纳西族自治县",530722:"永胜县",530723:"华坪县",530724:"宁蒗彝族自治县"},530800:{530802:"思茅区",530821:"宁洱哈尼族彝族自治县",530822:"墨江哈尼族自治县",530823:"景东彝族自治县",530824:"景谷傣族彝族自治县",530825:"镇沅彝族哈尼族拉祜族自治县",530826:"江城哈尼族彝族自治县",530827:"孟连傣族拉祜族佤族自治县",530828:"澜沧拉祜族自治县",530829:"西盟佤族自治县"},530900:{530902:"临翔区",530921:"凤庆县",530922:"云县",530923:"永德县",530924:"镇康县",530925:"双江拉祜族佤族布朗族傣族自治县",530926:"耿马傣族佤族自治县",530927:"沧源佤族自治县"},532300:{532301:"楚雄市",532322:"双柏县",532323:"牟定县",532324:"南华县",532325:"姚安县",532326:"大姚县",532327:"永仁县",532328:"元谋县",532329:"武定县",532331:"禄丰县"},532500:{532501:"个旧市",532502:"开远市",532522:"蒙自市",532523:"屏边苗族自治县",532524:"建水县",532525:"石屏县",532526:"弥勒市",532527:"泸西县",532528:"元阳县",532529:"红河县",532530:"金平苗族瑶族傣族自治县",532531:"绿春县",532532:"河口瑶族自治县"},532600:{532621:"文山市",532622:"砚山县",532623:"西畴县",532624:"麻栗坡县",532625:"马关县",532626:"丘北县",532627:"广南县",532628:"富宁县"},532800:{532801:"景洪市",532822:"勐海县",532823:"勐腊县"},532900:{532901:"大理市",532922:"漾濞彝族自治县",532923:"祥云县",532924:"宾川县",532925:"弥渡县",532926:"南涧彝族自治县",532927:"巍山彝族回族自治县",532928:"永平县",532929:"云龙县",532930:"洱源县",532931:"剑川县",532932:"鹤庆县"},533100:{533102:"瑞丽市",533103:"芒市",533122:"梁河县",533123:"盈江县",533124:"陇川县"},533300:{533321:"泸水县",533323:"福贡县",533324:"贡山独龙族怒族自治县",533325:"兰坪白族普米族自治县"},533400:{533421:"香格里拉市",533422:"德钦县",533423:"维西傈僳族自治县"},54e4:{540100:"拉萨市",542300:"日喀则市",542100:"昌都市",542200:"山南地区",542400:"那曲地区",542500:"阿里地区",542600:"林芝市"},540100:{540102:"城关区",540121:"林周县",540122:"当雄县",540123:"尼木县",540124:"曲水县",540125:"堆龙德庆县",540126:"达孜县",540127:"墨竹工卡县"},542300:{542301:"桑珠孜区",542322:"南木林县",542323:"江孜县",542324:"定日县",542325:"萨迦县",542326:"拉孜县",542327:"昂仁县",542328:"谢通门县",542329:"白朗县",542330:"仁布县",542331:"康马县",542332:"定结县",542333:"仲巴县",542334:"亚东县",542335:"吉隆县",542336:"聂拉木县",542337:"萨嘎县",542338:"岗巴县"},542100:{542121:"卡若区",542122:"江达县",542123:"贡觉县",542124:"类乌齐县",542125:"丁青县",542126:"察雅县",542127:"八宿县",542128:"左贡县",542129:"芒康县",542132:"洛隆县",542133:"边坝县"},542200:{542221:"乃东县",542222:"扎囊县",542223:"贡嘎县",542224:"桑日县",542225:"琼结县",542226:"曲松县",542227:"措美县",542228:"洛扎县",542229:"加查县",542231:"隆子县",542232:"错那县",542233:"浪卡子县"},542400:{542421:"那曲县",542422:"嘉黎县",542423:"比如县",542424:"聂荣县",542425:"安多县",542426:"申扎县",542427:"索县",542428:"班戈县",542429:"巴青县",542430:"尼玛县",542432:"双湖县"},542500:{542521:"普兰县",542522:"札达县",542523:"噶尔县",542524:"日土县",542525:"革吉县",542526:"改则县",542527:"措勤县"},542600:{542621:"巴宜区",542622:"工布江达县",542623:"米林县",542624:"墨脱县",542625:"波密县",542626:"察隅县",542627:"朗县"},61e4:{610100:"西安市",610200:"铜川市",610300:"宝鸡市",610400:"咸阳市",610500:"渭南市",610600:"延安市",610700:"汉中市",610800:"榆林市",610900:"安康市",611e3:"商洛市"},610100:{610102:"新城区",610103:"碑林区",610104:"莲湖区",610111:"灞桥区",610112:"未央区",610113:"雁塔区",610114:"阎良区",610115:"临潼区",610116:"长安区",610117:"高陵区",610122:"蓝田县",610124:"周至县",610125:"户县",610126:"高陵区",610199:"曲江新区"},610200:{610202:"王益区",610203:"印台区",610204:"耀州区",610222:"宜君县"},610300:{610302:"渭滨区",610303:"金台区",610304:"陈仓区",610322:"凤翔县",610323:"岐山县",610324:"扶风县",610326:"眉县",610327:"陇县",610328:"千阳县",610329:"麟游县",610330:"凤县",610331:"太白县"},610400:{610402:"秦都区",610403:"杨陵区",610404:"渭城区",610422:"三原县",610423:"泾阳县",610424:"乾县",610425:"礼泉县",610426:"永寿县",610427:"彬县",610428:"长武县",610429:"旬邑县",610430:"淳化县",610431:"武功县",610481:"兴平市"},610500:{610502:"临渭区",610521:"华县",610522:"潼关县",610523:"大荔县",610524:"合阳县",610525:"澄城县",610526:"蒲城县",610527:"白水县",610528:"富平县",610581:"韩城市",610582:"华阴市"},610600:{610602:"宝塔区",610621:"延长县",610622:"延川县",610623:"子长县",610624:"安塞县",610625:"志丹县",610626:"吴起县",610627:"甘泉县",610628:"富县",610629:"洛川县",610630:"宜川县",610631:"黄龙县",610632:"黄陵县"},610700:{610702:"汉台区",610721:"南郑县",610722:"城固县",610723:"洋县",610724:"西乡县",610725:"勉县",610726:"宁强县",610727:"略阳县",610728:"镇巴县",610729:"留坝县",610730:"佛坪县"},610800:{610802:"榆阳区",610821:"神木县",610822:"府谷县",610823:"横山县",610824:"靖边县",610825:"定边县",610826:"绥德县",610827:"米脂县",610828:"佳县",610829:"吴堡县",610830:"清涧县",610831:"子洲县"},610900:{610902:"汉滨区",610921:"汉阴县",610922:"石泉县",610923:"宁陕县",610924:"紫阳县",610925:"岚皋县",610926:"平利县",610927:"镇坪县",610928:"旬阳县",610929:"白河县"},611e3:{611002:"商州区",611021:"洛南县",611022:"丹凤县",611023:"商南县",611024:"山阳县",611025:"镇安县",611026:"柞水县"},62e4:{620100:"兰州市",620200:"嘉峪关市",620300:"金昌市",620400:"白银市",620500:"天水市",620600:"武威市",620700:"张掖市",620800:"平凉市",620900:"酒泉市",621e3:"庆阳市",621100:"定西市",621200:"陇南市",622900:"临夏回族自治州",623e3:"甘南藏族自治州"},620100:{1000665:"兰州新区",620102:"城关区",620103:"七里河区",620104:"西固区",620105:"安宁区",620111:"红古区",620121:"永登县",620122:"皋兰县",620123:"榆中县"},620300:{620302:"金川区",620321:"永昌县"},620400:{620402:"白银区",620403:"平川区",620421:"靖远县",620422:"会宁县",620423:"景泰县"},620500:{620502:"秦州区",620503:"麦积区",620521:"清水县",620522:"秦安县",620523:"甘谷县",620524:"武山县",620525:"张家川回族自治县"},620600:{620602:"凉州区",620621:"民勤县",620622:"古浪县",620623:"天祝藏族自治县"},620700:{620702:"甘州区",620721:"肃南裕固族自治县",620722:"民乐县",620723:"临泽县",620724:"高台县",620725:"山丹县"},620800:{620802:"崆峒区",620821:"泾川县",620822:"灵台县",620823:"崇信县",620824:"华亭县",620825:"庄浪县",620826:"静宁县"},620900:{620902:"肃州区",620921:"金塔县",620922:"瓜州县",620923:"肃北蒙古族自治县",620924:"阿克塞哈萨克族自治县",620981:"玉门市",620982:"敦煌市"},621e3:{621002:"西峰区",621021:"庆城县",621022:"环县",621023:"华池县",621024:"合水县",621025:"正宁县",621026:"宁县",621027:"镇原县"},621100:{621102:"安定区",621121:"通渭县",621122:"陇西县",621123:"渭源县",621124:"临洮县",621125:"漳县",621126:"岷县"},621200:{621202:"武都区",621221:"成县",621222:"文县",621223:"宕昌县",621224:"康县",621225:"西和县",621226:"礼县",621227:"徽县",621228:"两当县"},622900:{622901:"临夏市",622921:"临夏县",622922:"康乐县",622923:"永靖县",622924:"广河县",622925:"和政县",622926:"东乡族自治县",622927:"积石山保安族东乡族撒拉族自治县"},623e3:{623001:"合作市",623021:"临潭县",623022:"卓尼县",623023:"舟曲县",623024:"迭部县",623025:"玛曲县",623026:"碌曲县",623027:"夏河县"},63e4:{630100:"西宁市",632100:"海东市",632200:"海北藏族自治州",632300:"黄南藏族自治州",632500:"海南藏族自治州",632600:"果洛藏族自治州",632700:"玉树藏族自治州",632800:"海西蒙古族藏族自治州"},630100:{630102:"城东区",630103:"城中区",630104:"城西区",630105:"城北区",630121:"大通回族土族自治县",630122:"湟中县",630123:"湟源县"},632100:{632121:"平安区",632122:"民和回族土族自治县",632123:"乐都区",632126:"互助土族自治县",632127:"化隆回族自治县",632128:"循化撒拉族自治县"},632200:{632221:"门源回族自治县",632222:"祁连县",632223:"海晏县",632224:"刚察县"},632300:{632321:"同仁县",632322:"尖扎县",632323:"泽库县",632324:"河南蒙古族自治县"},632500:{632521:"共和县",632522:"同德县",632523:"贵德县",632524:"兴海县",632525:"贵南县"},632600:{632621:"玛沁县",632622:"班玛县",632623:"甘德县",632624:"达日县",632625:"久治县",632626:"玛多县"},632700:{632721:"玉树市",632722:"杂多县",632723:"称多县",632724:"治多县",632725:"囊谦县",632726:"曲麻莱县"},632800:{632801:"格尔木市",632802:"德令哈市",632821:"乌兰县",632822:"都兰县",632823:"天峻县"},64e4:{640100:"银川市",640200:"石嘴山市",640300:"吴忠市",640400:"固原市",640500:"中卫市"},640100:{640104:"兴庆区",640105:"西夏区",640106:"金凤区",640121:"永宁县",640122:"贺兰县",640181:"灵武市"},640200:{640202:"大武口区",640205:"惠农区",640221:"平罗县"},640300:{640302:"利通区",640303:"红寺堡区",640323:"盐池县",640324:"同心县",640381:"青铜峡市"},640400:{640402:"原州区",640422:"西吉县",640423:"隆德县",640424:"泾源县",640425:"彭阳县"},640500:{640502:"沙坡头区",640521:"中宁县",640522:"海原县"},65e4:{650100:"乌鲁木齐市",650200:"克拉玛依市",652100:"吐鲁番市",652200:"哈密地区",652300:"昌吉回族自治州",652700:"博尔塔拉蒙古自治州",652800:"巴音郭楞蒙古自治州",652900:"阿克苏地区",653e3:"克孜勒苏柯尔克孜自治州",653100:"喀什地区",653200:"和田地区",654e3:"伊犁哈萨克自治州",654200:"塔城地区",654300:"阿勒泰地区",659001:"石河子市",659002:"阿拉尔市",659003:"图木舒克市",659004:"五家渠市",659005:"北屯市",659006:"铁门关市",659008:"可克达拉市"},650100:{650102:"天山区",650103:"沙依巴克区",650104:"新市区",650105:"水磨沟区",650106:"头屯河区",650107:"达坂城区",650109:"米东区",650121:"乌鲁木齐县"},650200:{650202:"独山子区",650203:"克拉玛依区",650204:"白碱滩区",650205:"乌尔禾区"},652100:{652101:"高昌区",652122:"鄯善县",652123:"托克逊县"},652200:{652201:"哈密市",652222:"巴里坤哈萨克自治县",652223:"伊吾县"},652300:{652301:"昌吉市",652302:"阜康市",652323:"呼图壁县",652324:"玛纳斯县",652325:"奇台县",652327:"吉木萨尔县",652328:"木垒哈萨克自治县"},652700:{652701:"博乐市",652702:"阿拉山口市",652722:"精河县",652723:"温泉县"},652800:{652801:"库尔勒市",652822:"轮台县",652823:"尉犁县",652824:"若羌县",652825:"且末县",652826:"焉耆回族自治县",652827:"和静县",652828:"和硕县",652829:"博湖县"},652900:{652901:"阿克苏市",652922:"温宿县",652923:"库车县",652924:"沙雅县",652925:"新和县",652926:"拜城县",652927:"乌什县",652928:"阿瓦提县",652929:"柯坪县"},653e3:{653001:"阿图什市",653022:"阿克陶县",653023:"阿合奇县",653024:"乌恰县"},653100:{653101:"喀什市",653121:"疏附县",653122:"疏勒县",653123:"英吉沙县",653124:"泽普县",653125:"莎车县",653126:"叶城县",653127:"麦盖提县",653128:"岳普湖县",653129:"伽师县",653130:"巴楚县",653131:"塔什库尔干塔吉克自治县"},653200:{653201:"和田市",653221:"和田县",653222:"墨玉县",653223:"皮山县",653224:"洛浦县",653225:"策勒县",653226:"于田县",653227:"民丰县"},654e3:{654002:"伊宁市",654003:"奎屯市",654004:"霍尔果斯市",654021:"伊宁县",654022:"察布查尔锡伯自治县",654023:"霍城县",654024:"巩留县",654025:"新源县",654026:"昭苏县",654027:"特克斯县",654028:"尼勒克县"},654200:{654201:"塔城市",654202:"乌苏市",654221:"额敏县",654223:"沙湾县",654224:"托里县",654225:"裕民县",654226:"和布克赛尔蒙古自治县"},654300:{654301:"阿勒泰市",654321:"布尔津县",654322:"富蕴县",654323:"福海县",654324:"哈巴河县",654325:"青河县",654326:"吉木乃县"},81e4:{810100:"九龙",810200:"香港岛",810300:"新界"},810100:{810101:"观塘区",810102:"黄大仙区",810103:"九龙城区",810104:"深水埗区",810105:"油尖旺区"},810200:{810201:"东区",810202:"南区",810203:"湾仔区",810204:"中西区"},810300:{810301:"北区",810302:"大埔区",810303:"葵青区",810304:"离岛区",810305:"荃湾区",810306:"沙田区",810307:"屯门区",810308:"西贡区",810309:"元朗区"},82e4:{820100:"离岛",820200:"澳门半岛"},820100:{820101:"嘉模堂区",820102:"路氹填海区",820103:"圣方济各堂区"},820200:{820201:"大堂区",820202:"风顺堂区",820203:"花地玛堂区",820204:"花王堂区",820205:"望德堂区"},83e4:{830100:"台北市",830200:"新北市", -830300:"桃园市",830400:"台中市",830500:"台南市",830600:"高雄市",830700:"基隆市",830800:"新竹市",830900:"嘉义市"},830100:{830101:"中正区",830102:"大同区",830103:"中山区",830104:"万华区",830105:"信义区",830106:"松山区",830107:"大安区",830108:"南港区",830109:"北投区",830110:"内湖区",830111:"士林区",830112:"文山区"},830200:{830201:"板桥区",830202:"土城区",830203:"新庄区",830204:"新店区",830205:"深坑区",830206:"石碇区",830207:"坪林区",830208:"乌来区",830209:"五股区",830210:"八里区",830211:"林口区",830212:"淡水区",830213:"中和区",830214:"永和区",830215:"三重区",830216:"芦洲区",830217:"泰山区",830218:"树林区",830219:"莺歌区",830220:"三峡区",830221:"汐止区",830222:"金山区",830223:"万里区",830224:"三芝区",830225:"石门区",830226:"瑞芳区",830227:"贡寮区",830228:"双溪区",830229:"平溪区"},830300:{830301:"桃园区",830302:"中坜区",830303:"平镇区",830304:"八德区",830305:"杨梅区",830306:"芦竹区",830307:"大溪区",830308:"龙潭区",830309:"龟山区",830310:"大园区",830311:"观音区",830312:"新屋区",830313:"复兴区"},830400:{830401:"中区",830402:"东区",830403:"西区",830404:"南区",830405:"北区",830406:"西屯区",830407:"南屯区",830408:"北屯区",830409:"丰原区",830410:"大里区",830411:"太平区",830412:"东势区",830413:"大甲区",830414:"清水区",830415:"沙鹿区",830416:"梧栖区",830417:"后里区",830418:"神冈区",830419:"潭子区",830420:"大雅区",830421:"新小区",830422:"石冈区",830423:"外埔区",830424:"大安区",830425:"乌日区",830426:"大肚区",830427:"龙井区",830428:"雾峰区",830429:"和平区"},830500:{830501:"中西区",830502:"东区",830503:"南区",830504:"北区",830505:"安平区",830506:"安南区",830507:"永康区",830508:"归仁区",830509:"新化区",830510:"左镇区",830511:"玉井区",830512:"楠西区",830513:"南化区",830514:"仁德区",830515:"关庙区",830516:"龙崎区",830517:"官田区",830518:"麻豆区",830519:"佳里区",830520:"西港区",830521:"七股区",830522:"将军区",830523:"学甲区",830524:"北门区",830525:"新营区",830526:"后壁区",830527:"白河区",830528:"东山区",830529:"六甲区",830530:"下营区",830531:"柳营区",830532:"盐水区",830533:"善化区",830534:"大内区",830535:"山上区",830536:"新市区",830537:"安定区"},830600:{830601:"楠梓区",830602:"左营区",830603:"鼓山区",830604:"三民区",830605:"盐埕区",830606:"前金区",830607:"新兴区",830608:"苓雅区",830609:"前镇区",830610:"旗津区",830611:"小港区",830612:"凤山区",830613:"大寮区",830614:"鸟松区",830615:"林园区",830616:"仁武区",830617:"大树区",830618:"大社区",830619:"冈山区",830620:"路竹区",830621:"桥头区",830622:"梓官区",830623:"弥陀区",830624:"永安区",830625:"燕巢区",830626:"阿莲区",830627:"茄萣区",830628:"湖内区",830629:"旗山区",830630:"美浓区",830631:"内门区",830632:"杉林区",830633:"甲仙区",830634:"六龟区",830635:"茂林区",830636:"桃源区",830637:"那玛夏区"},830700:{830701:"中正区",830702:"七堵区",830703:"暖暖区",830704:"仁爱区",830705:"中山区",830706:"安乐区",830707:"信义区"},830800:{830801:"东区",830802:"北区",830803:"香山区"},830900:{830901:"东区",830902:"西区"}};"undefined"!=typeof window&&(window.ChineseDistricts=ChineseDistricts),define("plugins/lay-module/cityPicker/city-picker-data",function(){}),define("plugins/require-css/css.min!plugins/lay-module/cityPicker/city-picker",[],function(){}),layui.define(["jquery"],function(exports){var $=layui.$;if(void 0===ChineseDistricts)throw new Error('The file "city-picker.data.js" must be included first!');var EVENT_CHANGE="change.citypicker",CityPicker=function(element,options){this.PROVINCE="provinceId",this.CITY="cityId",this.DISTRICT="districtId",this.$element=$(element),this.$dropdown=null,this.options=$.extend({},CityPicker.DEFAULTS,$.isPlainObject(options)&&options),this.active=!1,this.dems=[],this.needBlur=!1,this.init()};CityPicker.prototype={constructor:CityPicker,init:function(){this.defineDems(),this.render(),this.bind(),this.active=!0;var _this=this;$(".icon_ca").on("click",function(){$("#"+this.PROVINCE).val(),$("#"+this.CITY).val(),$("#"+this.DISTRICT).val(),_this.reset()})},render:function(){var p=this.getPosition(),placeholder=this.$element.attr("placeholder")||this.options.placeholder,textspan=''+(placeholder?''+placeholder+"":"")+'
              ',dropdown='
              省份'+(this.includeDem(this.CITY)?'城市':"")+(this.includeDem(this.DISTRICT)?'区县':"")+'
              '+(this.includeDem(this.CITY)?'
              ':"")+(this.includeDem(this.DISTRICT)?'
              ':"")+"
              ";this.$element.addClass("city-picker-input"),this.$textspan=$(textspan).insertBefore(this.$element),this.$dropdown=$(dropdown).insertAfter(this.$textspan);var $select=this.$dropdown.find(".city-select");$.each(this.dems,$.proxy(function(i,type){this["$"+type]=$select.filter("."+type)},this)),this.refresh()},refresh:function(force){this.$dropdown.find(".city-select").data("item",null);var val=this.$element.val()||"";val=val.split("/"),$.each(this.dems,$.proxy(function(i,type){val[i]&&i.99&&(w=1),w=100*w+"%"),{top:p.top||0,left:p.left||0,height:h,width:w}},getSize:function($dom){var $wrap,$clone,sizes;return $dom.is(":visible")?sizes={width:$dom.outerWidth(),height:$dom.outerHeight()}:($wrap=$("
              ").appendTo($("body")),$wrap.css({position:"absolute !important",visibility:"hidden !important",display:"block !important"}),$clone=$dom.clone().appendTo($wrap),sizes={width:$clone.outerWidth(),height:$clone.outerHeight()},$wrap.remove()),sizes},getWidthStyle:function(w,dropdown){return w="100%",this.options.responsive&&!$.isNumeric(w)?"width:"+w+";":"width:"+(dropdown?Math.max(320,w):w)+"px;"},bind:function(){var $this=this;$(document).on("click",this._mouteclick=function(e){var $dropdown,$span,$input,$target=$(e.target);$target.is(".city-picker-span")?$span=$target:$target.is(".city-picker-span *")&&($span=$target.parents(".city-picker-span")),$target.is(".city-picker-input")&&($input=$target),$target.is(".city-picker-dropdown")?$dropdown=$target:$target.is(".city-picker-dropdown *")&&($dropdown=$target.parents(".city-picker-dropdown")),(!$input&&!$span&&!$dropdown||$span&&$span.get(0)!==$this.$textspan.get(0)||$input&&$input.get(0)!==$this.$element.get(0)||$dropdown&&$dropdown.get(0)!==$this.$dropdown.get(0))&&$this.close(!0)}),this.$element.on("change",this._changeElement=$.proxy(function(){this.close(!0),this.refresh(!0)},this)).on("focus",this._focusElement=$.proxy(function(){this.needBlur=!0,this.open()},this)).on("blur",this._blurElement=$.proxy(function(){this.needBlur&&(this.needBlur=!1,this.close(!0))},this)),this.$textspan.on("click",function(e){var type,$target=$(e.target);$this.needBlur=!1,$target.is(".select-item")?(type=$target.data("count"),$this.open(type)):$this.$dropdown.is(":visible")?$this.close():$this.open()}).on("mousedown",function(){$this.needBlur=!1}),this.$dropdown.on("click",".city-select a",function(){var $select=$(this).parents(".city-select"),$active=$select.find("a.active"),last=0===$select.next().length;$active.removeClass("active"),$(this).addClass("active"),$active.data("code")!==$(this).data("code")&&($select.data("item",{address:$(this).attr("title"),code:$(this).data("code")}),$(this).trigger(EVENT_CHANGE),$this.feedText(),$this.feedVal(),last&&$this.close())}).on("click",".city-select-tab a",function(){if(!$(this).hasClass("active")){var type=$(this).data("count");$this.tab(type)}}).on("mousedown",function(){$this.needBlur=!1}),this["$"+this.PROVINCE]&&this["$"+this.PROVINCE].on(EVENT_CHANGE,this._changeProvince=$.proxy(function(){this.output(this.CITY),this.output(this.DISTRICT),this.tab(this.CITY)},this)),this["$"+this.CITY]&&this["$"+this.CITY].on(EVENT_CHANGE,this._changeCity=$.proxy(function(){this.output(this.DISTRICT),this.tab(this.DISTRICT)},this))},open:function(type){type=type||this.PROVINCE,this.$dropdown.show(),this.$textspan.addClass("open").addClass("focus"),this.tab(type)},close:function(blur){this.$dropdown.hide(),this.$textspan.removeClass("open"),blur&&this.$textspan.removeClass("focus")},unbind:function(){$(document).off("click",this._mouteclick),this.$element.off("change",this._changeElement),this.$element.off("focus",this._focusElement),this.$element.off("blur",this._blurElement),this.$textspan.off("click"),this.$textspan.off("mousedown"),this.$dropdown.off("click"),this.$dropdown.off("mousedown"),this["$"+this.PROVINCE]&&this["$"+this.PROVINCE].off(EVENT_CHANGE,this._changeProvince),this.$city&&this.$city.off(EVENT_CHANGE,this._changeCity)},getText:function(){var text="";return this.$dropdown.find(".city-select").each(function(){var item=$(this).data("item"),type=$(this).data("count");item&&(text+=($(this).hasClass("province")?"":"/")+''+item.address+'')}),text},getPlaceHolder:function(){return this.$element.attr("placeholder")||this.options.placeholder},feedText:function(){this.getText()?(this.$textspan.find(">.placeholder").hide(),this.$textspan.find(">.title").children("input").val(""),this.$textspan.find(">.title").html(this.getText()).show()):(this.$textspan.find(">.placeholder").text(this.getPlaceHolder()).show(),this.$textspan.find(">.title").html("").hide())},getVal:function(){var text="";return this.$dropdown.find(".city-select").each(function(){var item=$(this).data("item");item&&(text+=($(this).hasClass("province")?"":"/")+item.address)}),text},feedVal:function(){this.$element.val(this.getVal())},output:function(type){var item,districts,code,value,options=this.options,PROVINCE=this.PROVINCE,CITY=this.CITY,DISTRICT=this.DISTRICT,$select=this["$"+type],data=type===PROVINCE?{}:[],matched=null;$select&&$select.length&&(item=$select.data("item"),value=(item?item.address:null)||options[type],code=type===PROVINCE?86:type===CITY?this["$"+PROVINCE]&&this["$"+PROVINCE].find(".active").data("code"):type===DISTRICT?this["$"+CITY]&&this["$"+CITY].find(".active").data("code"):code,districts=$.isNumeric(code)?ChineseDistricts[code]:null,$.isPlainObject(districts)&&$.each(districts,function(code,address){var provs;if(type===PROVINCE){provs=[];for(var i=0;i'),list.push("
              "+i+"
              "),$.each(n,function(j,m){list.push(''+(simple?$this.simplize(m.address,PROVINCE):m.address)+"")}),list.push("
              ")}),list.join("")},getList:function(data,type){var list=[],$this=this,simple=this.options.simple;return list.push('
              '),$.each(data,function(i,n){list.push(''+(simple?$this.simplize(n.address,type):n.address)+"")}),list.push("
              "),list.join("")},simplize:function(address,type){return address=address||"",type===this.PROVINCE?address.replace(/[省,市,自治区,壮族,回族,维吾尔]/g,""):type===this.CITY?address.replace(/[市,地区,回族,蒙古,苗族,白族,傣族,景颇族,藏族,彝族,壮族,傈僳族,布依族,侗族]/g,"").replace("哈萨克","").replace("自治州","").replace(/自治县/,""):type===this.DISTRICT?address.length>2?address.replace(/[市,区,县,旗]/g,""):address:void 0},tab:function(type){var $selects=this.$dropdown.find(".city-select"),$tabs=this.$dropdown.find(".city-select-tab > a"),$select=this["$"+type],$tab=this.$dropdown.find('.city-select-tab > a[data-count="'+type+'"]');$select&&($selects.hide(),$select.show(),$tabs.removeClass("active"),$tab.addClass("active"))},reset:function(){this.$element.val(null).trigger("change")},setValue:function(address){this.$element.val(address).trigger("change")},destroy:function(){this.unbind(),this.$element.removeData("citypicker").removeClass("city-picker-input"),this.$textspan.remove(),this.$dropdown.remove()}},CityPicker.DEFAULTS={simple:!1,responsive:!1,placeholder:"请选择省/市/区",level:"district",provincename:"",cityname:"",districtname:"",province:"河南省",city:"",district:""},CityPicker.setDefaults=function(options){$.extend(CityPicker.DEFAULTS,options)},CityPicker.other=$.fn.citypicker,$.fn.citypicker=function(option){var args=[].slice.call(arguments,1);return this.each(function(){var options,fn,$this=$(this),data=$this.data("citypicker");if(!data){if(/destroy/.test(option))return;options=$.extend({},$this.data(),$.isPlainObject(option)&&option),$this.data("citypicker",data=new CityPicker(this,options))}"string"==typeof option&&$.isFunction(fn=data[option])&&fn.apply(data,args)})},$.fn.citypicker.Constructor=CityPicker,$.fn.citypicker.setDefaults=CityPicker.setDefaults,$.fn.citypicker.noConflict=function(){return $.fn.citypicker=CityPicker.other,this},exports("cityPicker",CityPicker)}),define("cityPicker",["plugins/lay-module/cityPicker/city-picker-data","css!plugins/lay-module/cityPicker/city-picker.css"],function(){}),layui.define([],function(exports){function toDate(input){return input instanceof Date?input:isNaN(input)?/^\d+$/.test(input)?new Date(toInt(input)):(input=(input||"").trim().replace(/\.\d+/,"").replace(/-/,"/").replace(/-/,"/").replace(/(\d)T(\d)/,"$1 $2").replace(/Z/," UTC").replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"),new Date(input)):new Date(toInt(input))}function toInt(f){return parseInt(f)}function formatDiff(diff,locale,defaultLocale){locale=locales[locale]?locale:locales[defaultLocale]?defaultLocale:"zh_CN";for(var i=0,agoin=diff<0?1:0,total_sec=diff=Math.abs(diff);diff>=SEC_ARRAY[i]&&i(0===i?9:1)&&(i+=1),locales[locale](diff,i,total_sec)[agoin].replace("%s",diff)}function diffSec(date,nowDate){return((nowDate=nowDate?toDate(nowDate):new Date)-toDate(date))/1e3}function nextInterval(diff){for(var rst=1,i=0,d=Math.abs(diff);diff>=SEC_ARRAY[i]&&i1&&(unit+="s"),[number+" "+unit+" ago","in "+number+" "+unit]},zh_CN:function(number,index){if(0===index)return["刚刚","片刻后"];var unit=indexMapZh[parseInt(index/2)];return[number+unit+"前",number+unit+"后"]}},SEC_ARRAY=[60,60,24,7,365/7/12,12],SEC_ARRAY_LEN=6,ATTR_DATETIME="datetime",ATTR_DATA_TID="data-tid",timers={};Timeago.prototype.doRender=function(node,date,locale){var tid,diff=diffSec(date,this.nowDate),self=this;node.innerHTML=formatDiff(diff,locale,this.defaultLocale),timers[tid=setTimeout(function(){self.doRender(node,date,locale),delete timers[tid]},Math.min(1e3*nextInterval(diff),2147483647))]=0,setTidAttr(node,tid)},Timeago.prototype.format=function(date,locale){return formatDiff(diffSec(date,this.nowDate),locale,this.defaultLocale)},Timeago.prototype.render=function(nodes,locale){void 0===nodes.length&&(nodes=[nodes]);for(var i=0,len=nodes.length;i=self.selectArray.length)){var _selectData,_valueIndex,_queryName,_selectName,_selectValue,_select=self.selectArray[index],_dataUrl=_select.data("url"),_jsonSpace=void 0===_select.data("jsonSpace")?self.settings.jsonSpace:_select.data("jsonSpace"),_query={};if(cxSelect.clear.call(self,index),"string"==typeof _dataUrl&&_dataUrl.length){if(index>0)for(var i=0,j=1;i'+String(_firstTitle)+"";if("string"==typeof _jsonName&&_jsonName.length){"string"==typeof _jsonValue&&_jsonValue.length||(_jsonValue=_jsonName);for(var i=0,l=data.length;i'+String(data[i][_jsonName])+""}else for(var i=0,l=data.length;i'+String(data[i])+"";_select.html(_html).prop("disabled",!1).css({display:"",visibility:""}),"string"==typeof _select.attr("data-value")&&(_select.val(String(_select.attr("data-value"))).removeAttr("data-value"),_select[0].selectedIndex<0&&(_select[0].options[0].selected=!0)),(_required||_select[0].selectedIndex>0)&&_select.trigger("change"),form.render("select")}},cxSelect.selectChange=function(name){var self=this;if("string"==typeof name&&name.length){var index;name=name.replace(/\s+/g,","),name=","+name+",";for(var i=0,l=self.selectArray.length;i-1){index=i;break}"number"==typeof index&&index>-1&&(index+=1,cxSelect.getOptionData.call(self,index))}},$.cxSelect=function(){return cxSelect.apply(this,arguments)},$.cxSelect.defaults={selects:[],url:null,data:null,emptyStyle:null,required:!1,firstTitle:"请选择",firstValue:"",jsonSpace:"",jsonName:"n",jsonValue:"",jsonSub:"s"},$.fn.cxSelect=function(settings,callback){return this.each(function(i){$.cxSelect(this,settings,callback)}),this}}),exports("cxSelect",{})}),layui.define(["jquery","dropdown"],function(exports){"use strict";var $=layui.jquery,laytpl=layui.laytpl,dropdown=layui.dropdown,select={config:{valueSeparator:",",keywordPlaceholder:"请输入关键词",unfilteredText:"没有匹配的选项",customName:{id:"id",title:"title",selected:"selected"},options:[],allowCreate:!0,collapseSelected:!1,url:void 0,parseOptions:void 0}},thisSelect=function(){var that=this;return{config:that.config,reload:function(config){that.reload.call(that,config)},val:function(value){if(void 0===value)return that.context.selectedIds.join(that.config.valueSeparator);that.clear();for(var ids=$.isArray(value)?value:value.split(that.config.valueSeparator),i=0;i0&&(width=layuiInputBlock.width());var layuiInputGroup=that.config.elem.parents(".layui-input-group");layuiInputGroup.length>0&&(width-=layuiInputGroup.width()),that.config.elem.wrap('
              '),that.config.elem.after('
              '),that.render()};Class.prototype.render=function(){var that=this;if(that.config.url)return void $.get(that.config.url,{},function(res){var options=[];options=that.config.parseOptions?that.config.parseOptions(res):res,that.doRender(options)});that.doRender(that.config.options)},Class.prototype.doRender=function(options){var that=this,initOptions=options.map(function(option){var opt={};return opt[that.config.customName.id]=option[that.config.customName.id],opt[that.config.customName.title]=option[that.config.customName.title],opt[that.config.customName.selected]=option[that.config.customName.selected],opt}),selectedIds=initOptions.filter(function(option){return option[that.config.customName.selected]}).map(function(option){return option[that.config.customName.id]});that.config.elem.val(selectedIds.join(that.config.valueSeparator)),that.config.elem.parent().on("click",".multiple-select-selection-item-remove",function(e){e.stopPropagation();var id=$(this).parent().data("id"),option=that.getOptionById(id);that.remove(option)}),that.context={keyword:"",filteredOptions:null,selectedIds:selectedIds,options:initOptions,dropdownMenuScrollTop:0},that.renderDropdown(),that.renderSelection()},Class.prototype.clear=function(){this.context.selectedIds=[],this.reloadDropdownData(this.buildRenderOptions())},Class.prototype.remove=function(option){var that=this;this.context.selectedIds=this.context.selectedIds.filter(function(id){return id!=option[that.config.customName.id]}),this.reloadDropdownData(this.buildRenderOptions())},Class.prototype.select=function(options){options=layui.isArray(options)?options:[options];for(var length=options.length,i=0;i',tableBox+='',tableBox+="",tableBox+='',tableBox+="
              ",tableBox+='
              ',tableBox+="
              ",tableBox=$(tableBox),$("body").append(tableBox);var checkedData=[];opt.table.elem="#"+tableName,opt.table.id=tableName,opt.table.done=function(res,curr,count){defaultChecked(res,curr,count),setChecked(res,curr,count),tableDone(res,curr,count)};var tableSelect_table=table.render(opt.table);table.on("radio("+tableName+")",function(obj){opt.checkedKey&&(checkedData=table.checkStatus(tableName).data),updataButton(table.checkStatus(tableName).data.length)}),table.on("checkbox("+tableName+")",function(obj){if(opt.checkedKey){if(obj.checked)for(var i=0;i$(window).height(),overWidth=elem.offset().left+tableBox.outerWidth()>$(window).width();overHeight&&tableBox.css({top:"auto",bottom:"0px"}),overWidth&&tableBox.css({left:"auto",right:"5px"}),form.on("submit(tableSelect_btn_search)",function(data){return tableSelect_table.reload({where:data.field,page:{curr:1}}),!1}),table.on("rowDouble("+tableName+")",function(obj){selectDone({data:[obj.data]})}),tableBox.find(".tableSelect_btn_select").on("click",function(){var checkStatus=table.checkStatus(tableName);checkedData.length>1&&(checkStatus.data=checkedData),selectDone(checkStatus)}),$(document).mouseup(function(e){id=$(opt.elem).attr("id");var userSet_con=$(id+",.tableSelect");userSet_con.is(e.target)||0!==userSet_con.has(e.target).length||(tableBox.remove(),delete table.cache[tableName],checkedData=[])})})},tableSelect.prototype.hide=function(opt){$(".tableSelect").remove()};var tableSelect=new tableSelect;window.top==window.self&&$(window).scroll(function(){tableSelect.hide()}),exports("tableSelect",tableSelect)}),define("tableSelect",function(){}),define("plugins/require-css/css.min!plugins/lay-module/tableFilter/tableFilter",[],function(){}),layui.define(["table","jquery","form","laydate"],function(exports){var $=layui.jquery,table=layui.table,form=layui.form,laydate=layui.laydate,tableFilter={v:"1.1.0"};tableFilter.cache={},tableFilter.render=function(opt){var elem=$(opt.elem||"#table"),elemId=elem.attr("id")||"table_"+(new Date).getTime(),filters=opt.filters||[],parent=opt.parent||"body",mode=opt.mode||"local";opt.done=opt.done||function(){},tableFilter.cache[elemId]={};var main=function(){if("local"==mode){var trsIndex=tableFilter.getShowTrIndex(elem,elemId,filters);if(trsIndex.length>0){elem.next().find(".layui-table-body tr").each(function(i,tr){-1!=$.inArray($(tr).data("index"),trsIndex)?$(tr).removeClass("layui-hide"):$(tr).addClass("layui-hide")})}else elem.next().find(".layui-table-body tr").removeClass("layui-hide");tableFilter.fixAll(elem),table.resize(elemId)}layui.each(filters,function(i,filter){var filterField=filter.field,filterName=filter.name||filter.field,filterType=filter.type||"input",filterData=filter.data||[],filterUrl=filter.url||"",th=elem.next().find('.layui-table-header th[data-field="'+filterField+'"]'),icon="input"==filterType?"layui-icon-search":"layui-icon-down",filterIcon=$('');th.find(".layui-table-cell").append(filterIcon),tableFilter.cache[elemId][filterName]?filterIcon.addClass("tableFilter-has"):filterIcon.removeClass("tableFilter-has"),filterIcon.on("click",function(e){e.stopPropagation(),filter.data||filterUrl||"input"==filterType||(filterData=tableFilter.eachTds(elem,filterField));var t=$(this).offset().top+$(parent).scrollTop()+$(this).outerHeight()+5+"px",l_fix="date"==filterType?530:164,l=$(this).offset().left-($("body").outerWidth(!0)-$(parent).outerWidth(!0))-l_fix+"px",filterBox=$('
              ');if("input"==filterType&&filterBox.find("form").append(''),"checkbox"==filterType&&(filterBox.find("form").append("
                "),filterUrl||layui.each(filterData,function(i,item){filterBox.find("ul").append('
              • ')})),"radio"==filterType&&(filterBox.find("form").append('
                  '),filterUrl||(filterBox.find("ul").append('
                • '),layui.each(filterData,function(i,item){filterBox.find("ul").append('
                • ')}))),"date"==filterType&&(filterBox.find("form").append('
                  '),filterBox.find("form").append('')),filterBox.find("form").append(''),filterBox.find("form").append(''),$(this).hasClass("tableFilter-has")&&filterBox.find(".filter-del").removeClass("layui-btn-disabled").removeAttr("disabled","disabled"),$(parent).append(filterBox),form.val("table-filter-form",tableFilter.toLayuiFrom(elemId,filterName,filterType)),form.render(null,"table-filter-form"),"date"==filterType&&laydate.render({elem:".layui-table-filter-date",range:!0,type:"date",value:$(".layui-table-filter-date").next().val(),position:"static",showBottom:!1,change:function(value,date,endDate){$(".layui-table-filter-date").next().val(value)}}),filterBox.find('form input[type="search"]').focus().select(),("checkbox"==filterType||"radio"==filterType)&&filterUrl){var filterBoxUl=filterBox.find(".layui-table-filter-box ul");filterBoxUl.append('
                  '),$.getJSON(filterUrl+"?_t="+(new Date).getTime(),function(res,status,xhr){filterBoxUl.empty(),"radio"==filterType&&filterBoxUl.append('
                • '),layui.each(res.data,function(i,item){"checkbox"==filterType&&filterBoxUl.append('
                • '),"radio"==filterType&&filterBoxUl.append('
                • ')}),form.render(null,"table-filter-form"),form.val("table-filter-form",tableFilter.toLayuiFrom(elemId,filterName,filterType))})}form.on("submit(tableFilter)",function(data){if("checkbox"==filterType){var NEWfield=[];for(var key in data.field)NEWfield.push(data.field[key]);data.field[filterName]=NEWfield}if(tableFilter.cache[elemId][filterName]=data.field[filterName],tableFilter.cache[elemId][filterName].length>0?filterIcon.addClass("tableFilter-has"):filterIcon.removeClass("tableFilter-has"),"local"==mode){var trsIndex=tableFilter.getShowTrIndex(elem,elemId,filters);if(trsIndex.length>0||data.field[filterName].length>0){elem.next().find(".layui-table-body tr").each(function(i,tr){-1!=$.inArray($(tr).data("index"),trsIndex)?$(tr).removeClass("layui-hide"):$(tr).addClass("layui-hide")})}else elem.next().find(".layui-table-body tr").removeClass("layui-hide");tableFilter.updataTotal(elem),tableFilter.upNumbers(elem),tableFilter.uncheck(elem),table.resize(elemId)}else if("api"==mode){var formatFilter={},formatOp={};for(var key in tableFilter.cache[elemId]){var filterKey=key,filterValue=tableFilter.cache[elemId][key];formatFilter[filterKey]=filterValue,formatOp[filterKey]="LIKE",$.isArray(filterValue)?(formatFilter[filterKey]=filterValue,formatOp[filterKey]="IN"):formatFilter[filterKey]=filterValue}$filter={filter:JSON.stringify(formatFilter),op:JSON.stringify(formatOp)},table.reload(elemId,{where:$filter})}return opt.done(tableFilter.cache[elemId]),filterBox.remove(),!1}),filterBox.find(".layui-table-filter-box .filter-del").on("click",function(e){if(delete tableFilter.cache[elemId][filterName],filterIcon.removeClass("tableFilter-has"),"local"==mode){var trsIndex=tableFilter.getShowTrIndex(elem,elemId,filters);if(trsIndex.length>0){elem.next().find(".layui-table-body tr").each(function(i,tr){-1!=$.inArray($(tr).data("index"),trsIndex)?$(tr).removeClass("layui-hide"):$(tr).addClass("layui-hide")})}else elem.next().find(".layui-table-body tr").removeClass("layui-hide");tableFilter.updataTotal(elem),tableFilter.upNumbers(elem),tableFilter.uncheck(elem),table.resize(elemId)}else if("api"==mode){var where={};where[filterName]="",table.reload(elemId,{where:where})}opt.done(tableFilter.cache[elemId]),filterBox.remove()}),$(document).mouseup(function(e){var userSet_con=$(".layui-table-filter-view");userSet_con.is(e.target)||0!==userSet_con.has(e.target).length||filterBox.remove()})})})};return main(),{config:opt,reload:function(opt){main(),tableFilter.upNumbers(elem)}}},tableFilter.eachTds=function(elem,filterField){var tdsText=[],tdsArray=[];return elem.next().find('.layui-table-body td[data-field="'+filterField+'"]').each(function(i,td){tdsText.push($.trim(td.innerText))}),tdsText=tableFilter.tool.uniqueObjArray(tdsText),layui.each(tdsText,function(i,item){tdsArray.push({key:item,value:item})}),tdsArray},tableFilter.getShowTrIndex=function(elem,elemId,filters){var trsIndex=[],filterValues=tableFilter.cache[elemId];for(var key in filterValues){var filterKey=key,filterValue=filterValues[key];layui.each(filters,function(i,item){filterKey==item.name&&(filterKey=item.field)});var tds=elem.next().find('.layui-table-body td[data-field="'+filterKey+'"]'),this_trsIndex=[];tds.each(function(i,td){if($.isArray(filterValue))$.inArray($.trim(td.innerText),filterValue)>=0&&filterValue&&filterValue.length>0&&this_trsIndex.push($(td).parent().data("index"));else if(filterValue.indexOf(" - ")>=0){var d=$.trim(td.innerText),s=filterValue.split(" - ")[0],e=filterValue.split(" - ")[1];tableFilter.tool.isDuringDate(d,s,e)&&this_trsIndex.push($(td).parent().data("index"))}else $.trim(td.innerText).indexOf(filterValue)>=0&&this_trsIndex.push($(td).parent().data("index"))}),trsIndex=trsIndex.length<=0?this_trsIndex:this_trsIndex.length>0?tableFilter.tool.getSameArray(trsIndex,this_trsIndex):$.isArray(filterValue)?trsIndex:[]}return tableFilter.tool.uniqueObjArray(trsIndex)},tableFilter.toLayuiFrom=function(elemId,filterName,filterType){var form_val=JSON.stringify(tableFilter.cache[elemId]);return form_val=JSON.parse(form_val),"checkbox"==filterType&&(layui.each(form_val[filterName],function(i,value){form_val[filterName+"["+value+"]"]=!0}),delete form_val[filterName]),form_val},tableFilter.updataTotal=function(elem){var elemId=elem.attr("id");table.eachCols(elemId,function(i,item){if(item.totalRow){var tdAllnum=0;elem.next().find('.layui-table-body td[data-field="'+item.field+'"]').each(function(i,td){$(td).parent().hasClass("layui-hide")||(tdAllnum=(10*tdAllnum+10*Number($.trim(td.innerText)))/10)});elem.next().find('.layui-table-total td[data-field="'+item.field+'"]').each(function(i,td){$(td).find(".layui-table-cell").html(tdAllnum||"0")})}})},tableFilter.upNumbers=function(elem){var cur=elem.next().find(".layui-laypage-curr").text();cur=Number(cur||"1");var limit=elem.next().find(".layui-laypage-limits select").val();limit=Number(limit);var trs=elem.next().find(".layui-table-main tr"),n=1==cur?0:limit*(cur-1);if(trs.each(function(i,tr){$(tr).hasClass("layui-hide")||(n+=1,$(tr).find(".laytable-cell-numbers").html(n))}),elem.next().find(".layui-table-fixed").length>=1){var trs_f=elem.next().find(".layui-table-fixed .layui-table-body tr"),n_f=1==cur?0:limit*(cur-1);trs_f.each(function(i,tr_f){$(tr_f).hasClass("layui-hide")||(n_f+=1,$(tr_f).find(".laytable-cell-numbers").html(n_f))})}},tableFilter.uncheck=function(elem){elem.attr("id"),elem.attr("lay-filter");elem.next().find(".layui-table-fixed-l tr").each(function(i,tr){$(tr).find("[name='layTableCheckbox']").prop("checked")&&$(tr).find(".layui-form-checked i").click()})},tableFilter.fixAll=function(elem){var elemId=elem.attr("id"),tableName=elem.attr("lay-filter"),trs=elem.next().find(".layui-table-main tr");table.on("checkbox("+tableName+")",function(obj){if("all"==obj.type){var data=table.cache[elemId];trs.each(function(i,tr){$(tr).hasClass("layui-hide")&&(data[i].LAY_CHECKED=!1)})}})},tableFilter.hide=function(){$(".layui-table-filter-view").remove()},tableFilter.tool={uniqueObjArray:function(arr,type){var newArr=[],tArr=[];if(0==arr.length)return arr;if(type){for(var i=0;i=beginDate&&curDate<=endDate}},exports("tableFilter",tableFilter)}),define("tableFilter",["css!plugins/lay-module/tableFilter/tableFilter.css"],function(){}),layui.define(["laypage","form"],function(exports){"use strict";var IconPicker=function(){this.v="0.1.beta"},$=layui.jquery;layui.laypage,layui.form;IconPicker.prototype.render=function(options){var opts=options,elem=opts.elem,type=null==opts.type?"fontClass":opts.type,page=opts.page,limit=null==limit?12:opts.limit,search=null==opts.search||opts.search,click=opts.click,data={},tmp=(new Date).getTime(),TITLE_ID="layui-select-title-"+tmp,ICON_BODY="layui-iconpicker-"+tmp,PICKER_BODY="layui-iconpicker-body-"+tmp,PAGE_ID="layui-iconpicker-page-"+tmp,LIST_BOX="layui-iconpicker-list-box",selected="layui-form-selected",value=options.value||$(elem).val()||"layui-icon layui-icon-circle-dot",i=' '+value+"";console.log(i);var a={init:function(){return data=common.getData[type](),a.hideElem().createSelect().createBody().toggleSelect(),common.loadCss(),a},hideElem:function(){return $(elem).hide(),a},createSelect:function(){var selectHtml='
                  '+i+'
                  123
                  ';return $(elem).after(selectHtml),a},toggleSelect:function(){var item="#"+TITLE_ID+" .layui-iconpicker-item,#"+TITLE_ID+" .layui-iconpicker-item .layui-edge";return a.event("click",item,function(e){var $icon=$("#"+ICON_BODY);$icon.hasClass(selected)?$icon.removeClass(selected).addClass("layui-unselect"):$icon.addClass(selected).removeClass("layui-unselect"),e.stopPropagation()}),a},createBody:function(){var searchHtml="";search&&(searchHtml='');var bodyHtml='
                  '+searchHtml+'
                  ';return $("#"+ICON_BODY).find(".layui-anim").eq(0).html(bodyHtml),a.search().createList().check().page(),a},createList:function(text){for(var d=data,l=d.length,pageHtml="",listHtml=$('
                  '),_limit=limit,_pages=l%_limit==0?l/_limit:parseInt(l/_limit+1),icons=[],i=0;i';icon+='',icon+="
                  ",icons.push(icon)}}l=icons.length,_pages=l%_limit==0?l/_limit:parseInt(l/_limit+1);for(var i=0;i<_pages;i++){for(var lm=$('
                  '),j=i*_limit;j<(i+1)*_limit&&j无数据

                  '),page&&($("#"+PICKER_BODY).addClass("layui-iconpicker-body-page"),pageHtml='
                  1/'+_pages+' ('+l+')
                  '),$("#"+ICON_BODY).find(".layui-anim").find("."+LIST_BOX).html("").append(listHtml).append(pageHtml),a},page:function(){var icon="#"+PAGE_ID+" .layui-iconpicker-page-operate .layui-icon";return $(icon).unbind("click"),a.event("click",icon,function(e){var elem=e.currentTarget,total=parseInt($("#"+PAGE_ID+"-pages").html()),isPrev=void 0!==$(elem).attr("prev"),$cur=(parseInt($(elem).attr("data-index")),$("#"+PAGE_ID+"-current")),current=parseInt($cur.html());isPrev&¤t>1?(current-=1,$(icon+"[prev]").attr("data-index",current)):!isPrev&¤t.layui-iconpicker {max-width: 280px;}.layui-iconpicker .layui-anim{display:none;position:absolute;left:0;top:42px;padding:5px 0;z-index:899;min-width:100%;border:1px solid #d2d2d2;max-height:300px;overflow-y:auto;background-color:#fff;border-radius:2px;box-shadow:0 2px 4px rgba(0,0,0,.12);box-sizing:border-box;}.layui-iconpicker-item{border:1px solid #e6e6e6;height:38px;border-radius:4px;cursor:pointer;position:relative;}.layui-iconpicker-icon{border-right:1px solid #e6e6e6;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;width:85%;height:100%;float:left;text-align:center;background:#fff;transition:all .3s;}.layui-iconpicker-icon i{line-height:38px;font-size:18px;}.layui-iconpicker-item:hover{border-color:#D2D2D2!important;}.layui-iconpicker-item:hover .layui-iconpicker-icon{border-color:#D2D2D2!important;}.layui-iconpicker.layui-form-selected .layui-anim{display:block;}.layui-iconpicker-body{padding:6px;}.layui-iconpicker .layui-iconpicker-list{background-color:#fff;border:1px solid #ccc;border-radius:4px;}.layui-iconpicker .layui-iconpicker-icon-item{display:inline-block;width:21.1%;line-height:36px;text-align:center;cursor:pointer;vertical-align:top;height:36px;margin:4px;border:1px solid #ddd;border-radius:2px;transition:300ms;}.layui-iconpicker .layui-iconpicker-icon-item i.layui-icon{font-size:17px;}.layui-iconpicker .layui-iconpicker-icon-item:hover{background-color:#eee;border-color:#ccc;-webkit-box-shadow:0 0 2px #aaa,0 0 2px #fff inset;-moz-box-shadow:0 0 2px #aaa,0 0 2px #fff inset;box-shadow:0 0 2px #aaa,0 0 2px #fff inset;text-shadow:0 0 1px #fff;}.layui-iconpicker-search{position:relative;margin:0 0 6px 0;border:1px solid #e6e6e6;border-radius:2px;transition:300ms;}.layui-iconpicker-search:hover{border-color:#D2D2D2!important;}.layui-iconpicker-search .layui-input{cursor:text;display:inline-block;width:86%;border:none;padding-right:0;margin-top:1px;}.layui-iconpicker-search .layui-icon{position:absolute;top:11px;right:4%;}.layui-iconpicker-tips{text-align:center;padding:8px 0;cursor:not-allowed;}.layui-iconpicker-page{margin-top:6px;margin-bottom:-6px;font-size:12px;padding:0 2px;}.layui-iconpicker-page-count{display:inline-block;}.layui-iconpicker-page-operate{display:inline-block;float:right;cursor:default;}.layui-iconpicker-page-operate .layui-icon{font-size:12px;cursor:pointer;}.layui-iconpicker-body-page .layui-iconpicker-icon-limit{display:none;}.layui-iconpicker-body-page .layui-iconpicker-icon-limit:first-child{display:block;}')},getData:{fontClass:function(){return["layui-icon-bot","layui-icon-leaf","layui-icon-folder","layui-icon-folder-open","layui-icon-gitee","layui-icon-github","layui-icon-moon","layui-icon-error","layui-icon-success","layui-icon-question","layui-icon-lock","layui-icon-eye","layui-icon-eye-invisible","layui-icon-clear","layui-icon-backspace","layui-icon-disabled","layui-icon-tips-fill","layui-icon-test","layui-icon-music","layui-icon-chrome","layui-icon-firefox","layui-icon-edge","layui-icon-ie","layui-icon-heart-fill","layui-icon-heart","layui-icon-light","layui-icon-time","layui-icon-bluetooth","layui-icon-at","layui-icon-mute","layui-icon-mike","layui-icon-key","layui-icon-gift","layui-icon-email","layui-icon-rss","layui-icon-wifi","layui-icon-logout","layui-icon-android","layui-icon-ios","layui-icon-windows","layui-icon-transfer","layui-icon-service","layui-icon-subtraction","layui-icon-addition","layui-icon-slider","layui-icon-print","layui-icon-export","layui-icon-cols","layui-icon-screen-restore","layui-icon-screen-full","layui-icon-rate-half","layui-icon-rate","layui-icon-rate-solid","layui-icon-cellphone","layui-icon-vercode","layui-icon-login-wechat","layui-icon-login-qq","layui-icon-login-weibo","layui-icon-password","layui-icon-username","layui-icon-refresh-3","layui-icon-auz","layui-icon-spread-left","layui-icon-shrink-right","layui-icon-snowflake","layui-icon-tips","layui-icon-note","layui-icon-home","layui-icon-senior","layui-icon-refresh","layui-icon-refresh-1","layui-icon-flag","layui-icon-theme","layui-icon-notice","layui-icon-website","layui-icon-console","layui-icon-face-surprised","layui-icon-set","layui-icon-template-1","layui-icon-app","layui-icon-template","layui-icon-praise","layui-icon-tread","layui-icon-male","layui-icon-female","layui-icon-camera","layui-icon-camera-fill","layui-icon-more","layui-icon-more-vertical","layui-icon-rmb","layui-icon-dollar","layui-icon-diamond","layui-icon-fire","layui-icon-return","layui-icon-location","layui-icon-read","layui-icon-survey","layui-icon-face-smile","layui-icon-face-cry","layui-icon-cart-simple","layui-icon-cart","layui-icon-next","layui-icon-prev","layui-icon-upload-drag","layui-icon-upload","layui-icon-download-circle","layui-icon-component","layui-icon-file-b","layui-icon-user","layui-icon-find-fill","layui-icon-loading","layui-icon-loading-1","layui-icon-add-1","layui-icon-play","layui-icon-pause","layui-icon-headset","layui-icon-video","layui-icon-voice","layui-icon-speaker","layui-icon-fonts-del","layui-icon-fonts-code","layui-icon-fonts-html","layui-icon-fonts-strong","layui-icon-unlink","layui-icon-picture","layui-icon-link","layui-icon-face-smile-b","layui-icon-align-left","layui-icon-align-right","layui-icon-align-center","layui-icon-fonts-u","layui-icon-fonts-i","layui-icon-tabs","layui-icon-radio","layui-icon-circle","layui-icon-edit","layui-icon-share","layui-icon-delete","layui-icon-form","layui-icon-cellphone-fine","layui-icon-dialogue","layui-icon-fonts-clear","layui-icon-layer","layui-icon-date","layui-icon-water","layui-icon-code-circle","layui-icon-carousel","layui-icon-prev-circle","layui-icon-layouts","layui-icon-util","layui-icon-templeate-1","layui-icon-upload-circle","layui-icon-tree","layui-icon-table","layui-icon-chart","layui-icon-chart-screen","layui-icon-engine","layui-icon-triangle-d","layui-icon-triangle-r","layui-icon-file","layui-icon-set-sm","layui-icon-reduce-circle","layui-icon-add-circle","layui-icon-404","layui-icon-about","layui-icon-up","layui-icon-down","layui-icon-left","layui-icon-right","layui-icon-circle-dot","layui-icon-search","layui-icon-set-fill","layui-icon-group","layui-icon-friends","layui-icon-reply-fill","layui-icon-menu-fill","layui-icon-log","layui-icon-picture-fine","layui-icon-face-smile-fine","layui-icon-list","layui-icon-release","layui-icon-ok","layui-icon-help","layui-icon-chat","layui-icon-top","layui-icon-star","layui-icon-star-fill","layui-icon-close-fill","layui-icon-close","layui-icon-ok-circle","layui-icon-add-circle-fine"]}}};return a.init(),new IconPicker},IconPicker.prototype.checkIcon=function(filter,iconName){var p=$("*[lay-filter="+filter+"]").next().find(".layui-iconpicker-item .layui-icon"),c=iconName;c.indexOf("#xe")>0?p.html(c):p.html("").attr("class",""+c)},exports("iconPicker",new IconPicker)}),define("iconPicker",function(){}),function(global,factory){"object"===("undefined"==typeof exports?"undefined":_typeof(exports))&&"undefined"!=typeof module?module.exports=factory():"function"==typeof define&&define.amd?define("toastr",factory):(global=global||self,global.notify=factory())}(void 0,function(){function c(args,children){var el=document.createElement("div");for(var key in args){var element=args[key];"className"==key?(key="class",el.setAttribute(key,element)):"_"==key[0]&&el.addEventListener(key.slice(1),element)}if("string"==typeof children)el.innerHTML=children;else if("object"==_typeof(children)&&children.tagName)el.appendChild(children);else if(children)for(var i=0;i-1}function removeClass(el,s){var c=el.className||"";if(hasClass(c,s)){var arr=c.split(/\s+/),i=arr.indexOf(s);arr.splice(i,1),el.className=arr.join(" ")}""===el.className&&el.removeAttribute("class")}function initConfig(obj,type){var args={};for(var key in initArgs)args[key]=initArgs[key];for(var posArr=["bottomRight","bottomLeft","topRight","topLeft","topCenter","bottomCenter","center"],i=0;i-1?args.position=it:args.msg=it:"boolean"==typeof it?args.showClose=it:"function"==typeof it?it.call(this):"number"==typeof it&&1!=i&&(args.duration=it))}return args.type=type,createMsgEl(args)}function createMsgEl(args){var _msgWrapper,type=args.type,duration=args.duration,msg=args.msg,position=args.position,closable=args.showClose,onClose=args.onClose,iconObj=getIconObj();document.getElementsByClassName(position)[0]?_msgWrapper=document.getElementsByClassName(position)[0]:(_msgWrapper=c({className:"notify-msg-stage "+position}),msgWrappers.push(_msgWrapper)),"loading"===type&&(msg=""===msg?"正在加载,请稍后":msg,closable=!1);var el;el=-1!=positionB.indexOf(position)?c({className:"notify-msg-wrapper"},[c({className:"notify-msg notify-bottom notify-msg-fade-in-b "+type},[c({className:"notify-msg-icon"},iconObj[type]),c({className:"notify-msg-content"},msg),c({className:"notify-msg-wait "+(closable?"notify-msg-pointer":""),_click:function(){closable&&(closeFlag=!0,flag=!1,closeMsg(el,onClose,_msgWrapper))}},getMsgRight(closable))])]):c({className:"notify-msg-wrapper"},[c({className:"notify-msg notify-msg-fade-in "+type},[c({className:"notify-msg-icon"},iconObj[type]),c({className:"notify-msg-content"},msg),c({className:"notify-msg-wait "+(closable?"notify-msg-pointer":""),_click:function(){closable&&(closeFlag=!0,flag=!1,closeMsg(el,onClose,_msgWrapper))}},getMsgRight(closable))])]);var anm=el.querySelector(".notify-msg__circle");if(anm&&(css(anm,{animation:"notify-msg_"+type+" "+duration+"ms linear"}),"onanimationend"in window?addAnimationEnd(anm,function(){closeMsg(el,onClose,_msgWrapper)}):setTimeout(function(){closeMsg(el,onClose,_msgWrapper)},duration)),"loading"!=type&&setTimeout(function(){closeMsg(el,onClose,_msgWrapper)},duration),_msgWrapper.children.length||document.body.appendChild(_msgWrapper),_msgWrapper.appendChild(el),css(el,{height:el.offsetHeight+"px"}),setTimeout(function(){-1!=positionB.indexOf(position)?removeClass(el.children[0],"notify-msg-fade-in-b"):removeClass(el.children[0],"notify-msg-fade-in")},300),"loading"==type)return function(){closeMsg(el,onClose,_msgWrapper)}}function getMsgRight(showClose){if(showClose)return'\n \n '}function closeMsg(el,cb,_msgWrapper){el&&(css(el,{padding:0,height:0}),$(el.children[0]).hasClass("notify-bottom")?addClass(el.children[0],"notify-msg-fade-out-b"):addClass(el.children[0],"notify-msg-fade-out"),closeFlag?(closeFlag=!1,cb&&cb()):flag?cb&&cb():flag=!0,setTimeout(function(){if(el){var has=!1;if(_msgWrapper){for(var i=0;i<_msgWrapper.children.length;i++)_msgWrapper.children[i]&&_msgWrapper.children[i]===el&&(has=!0);has&&removeChild(el),el=null,_msgWrapper.children.length||has&&removeChild(_msgWrapper)}}},300))}function getIconObj(){return{info:'\n \n ', +success:'\n \n ',warning:'\n \n ',error:'\n \n ',loading:'\n
                  \n \n \n \n
                  \n '}}function removeChild(el){el&&el.parentNode.removeChild(el)}function _destroyAll(){for(var j=0;j',i
                  ':'
                  ');var number=stepItems[i].number;number||(number=i+1),stepDiv+=i==postion?'
                  '+number+"
                  ":i
                  ':'
                  '+number+"
                  ";var title=stepItems[i].title,desc=stepItems[i].desc;(title||desc)&&(stepDiv+='
                  ',title&&(stepDiv+='
                  '+title+"
                  "),desc&&(stepDiv+='
                  '+desc+"
                  "),stepDiv+="
                  "),stepDiv+="
                  "}stepDiv+="
                ",$(elem).prepend(stepDiv);var bfb=100/stepItems.length;$(".step-item").css("width",bfb+"%")},step={render:function(param){param.indicator="none",param.arrow="always",param.autoplay=!1,param.stepWidth||(param.stepWidth="400px"),carousel.render(param);var stepItems=param.stepItems;renderDom(param.elem,stepItems,0),$(".lay-step").css("width",param.stepWidth),carousel.on("change("+param.filter+")",function(obj){$(param.elem).find(".lay-step").remove(),renderDom(param.elem,stepItems,obj.index),$(".lay-step").css("width",param.stepWidth)}),$(param.elem).find(".layui-carousel-arrow").css("display","none"),$(param.elem).css("background-color","transparent")},next:function(elem){$(elem).find(".layui-carousel-arrow[lay-type=add]").trigger("click")},pre:function(elem){$(elem).find(".layui-carousel-arrow[lay-type=sub]").trigger("click")}};layui.link("/static/plugins/lay-module/step-lay/step.css"),exports("step",step)}),define("step-lay",function(){});var _createClass=function(){function defineProperties(target,props){for(var i=0;i'+value+""+(this.options.permanentData.includes(value)?"":'×')+""}},{key:"onChange",value:function(value,type){this.options.onChange&&"function"==typeof this.options.onChange&&this.options.onChange(this.copyData,value,type)}},{key:"getData",value:function(){return this.copyData}},{key:"clearData",value:function(){this.options.data=[],this.elem.prevAll("span.layui-tag").remove()}}]),InputTag}();return{render:function(options){return new InputTag(options)}}})}("function"==typeof define&&define.amd?define:function(deps,factory){"undefined"!=typeof module&&module.exports?module.exports=factory(require("jquery")):window.layui&&layui.define?layui.define("jquery",function(exports){exports("inputTag",factory(layui.jquery))}):window.inputTag=factory(window.jQuery)}),layui.link("/static/plugins/lay-module/inputTags/inputTags.css?v=7");var ChineseDistricts={886:{11e4:"北京市",12e4:"天津市",13e4:"河北省",14e4:"山西省",15e4:"内蒙古自治区",21e4:"辽宁省",22e4:"吉林省",23e4:"黑龙江省",31e4:"上海市",32e4:"江苏省",33e4:"浙江省",34e4:"安徽省",35e4:"福建省",36e4:"江西省",37e4:"山东省",41e4:"河南省",42e4:"湖北省",43e4:"湖南省",44e4:"广东省",45e4:"广西壮族自治区",46e4:"海南省",5e5:"重庆市",51e4:"四川省",52e4:"贵州省",53e4:"云南省",54e4:"西藏自治区",61e4:"陕西省",62e4:"甘肃省",63e4:"青海省",64e4:"宁夏回族自治区",65e4:"新疆维吾尔自治区",83e4:"台湾省",81e4:"香港特别行政区",82e4:"澳门特别行政区"},86:{"A-G":[{code:"340000",address:"安徽省"},{code:"110000",address:"北京市"},{code:"500000",address:"重庆市"},{code:"350000",address:"福建省"},{code:"620000",address:"甘肃省"},{code:"440000",address:"广东省"},{code:"450000",address:"广西壮族自治区"},{code:"520000",address:"贵州省"},{code:"820000",address:"澳门特别行政区"}],"H-K":[{code:"460000",address:"海南省"},{code:"130000",address:"河北省"},{code:"230000",address:"黑龙江省"},{code:"410000",address:"河南省"},{code:"420000",address:"湖北省"},{code:"430000",address:"湖南省"},{code:"320000",address:"江苏省"},{code:"360000",address:"江西省"},{code:"220000",address:"吉林省"}],"L-S":[{code:"210000",address:"辽宁省"},{code:"150000",address:"内蒙古自治区"},{code:"640000",address:"宁夏回族自治区"},{code:"630000",address:"青海省"},{code:"370000",address:"山东省"},{code:"310000",address:"上海市"},{code:"140000",address:"山西省"},{code:"610000",address:"陕西省"},{code:"510000",address:"四川省"}],"T-Z":[{code:"120000",address:"天津市"},{code:"830000",address:"台湾省"},{code:"650000",address:"新疆维吾尔自治区"},{code:"540000",address:"西藏自治区"},{code:"810000",address:"香港特别行政区"},{code:"530000",address:"云南省"},{code:"330000",address:"浙江省"}]},11e4:{110100:"北京市"},110100:{110101:"东城区",110102:"西城区",110103:"崇文区",110104:"宣武区",110105:"朝阳区",110106:"丰台区",110107:"石景山区",110108:"海淀区",110109:"门头沟区",110111:"房山区",110112:"通州区",110113:"顺义区",110114:"昌平区",110115:"大兴区",110116:"怀柔区",110117:"平谷区",110228:"密云县",110229:"延庆县"},12e4:{120100:"天津市 "},120100:{120101:"和平区",120102:"河东区",120103:"河西区",120104:"南开区",120105:"河北区",120106:"红桥区",120107:"塘沽区",120108:"汉沽区",120109:"大港区",120110:"东丽区",120111:"西青区",120112:"津南区",120113:"北辰区",120114:"武清区",120115:"宝坻区",120116:"滨海新区",120221:"宁河县",120223:"静海县",120225:"蓟县"},13e4:{130100:"石家庄市",130200:"唐山市",130300:"秦皇岛市",130400:"邯郸市",130500:"邢台市",130600:"保定市",130700:"张家口市",130800:"承德市",130900:"沧州市",131e3:"廊坊市",131100:"衡水市"},130100:{130102:"长安区",130103:"桥东区",130104:"桥西区",130105:"新华区",130107:"井陉矿区",130108:"裕华区",130121:"井陉县",130123:"正定县",130124:"栾城区",130125:"行唐县",130126:"灵寿县",130127:"高邑县",130128:"深泽县",130129:"赞皇县",130130:"无极县",130131:"平山县",130132:"元氏县",130133:"赵县",130181:"辛集市",130182:"藁城区",130183:"晋州市",130184:"新乐市",130185:"鹿泉区"},130200:{130202:"路南区",130203:"路北区",130204:"古冶区",130205:"开平区",130207:"丰南区",130208:"丰润区",130223:"滦县",130224:"滦南县",130225:"乐亭县",130227:"迁西县",130229:"玉田县",130230:"曹妃甸区",130281:"遵化市",130283:"迁安市",130299:"唐海县"},130300:{130302:"海港区",130303:"山海关区",130304:"北戴河区",130321:"青龙满族自治县",130322:"昌黎县",130323:"抚宁县",130324:"卢龙县",130399:"经济技术开发区"},130400:{130402:"邯山区",130403:"丛台区",130404:"复兴区",130406:"峰峰矿区",130421:"邯郸县",130423:"临漳县",130424:"成安县",130425:"大名县",130426:"涉县",130427:"磁县",130428:"肥乡县",130429:"永年县",130430:"邱县",130431:"鸡泽县",130432:"广平县",130433:"馆陶县",130434:"魏县",130435:"曲周县",130481:"武安市"},130500:{130502:"桥东区",130503:"桥西区",130521:"邢台县",130522:"临城县",130523:"内丘县",130524:"柏乡县",130525:"隆尧县",130526:"任县",130527:"南和县",130528:"宁晋县",130529:"巨鹿县",130530:"新河县",130531:"广宗县",130532:"平乡县",130533:"威县",130534:"清河县",130535:"临西县",130581:"南宫市",130582:"沙河市"},130600:{130602:"新市区",130603:"北市区",130604:"南市区",130621:"满城区",130622:"清苑区",130623:"涞水县",130624:"阜平县",130625:"徐水区",130626:"定兴县",130627:"唐县",130628:"高阳县",130629:"容城县",130630:"涞源县",130631:"望都县",130632:"安新县",130633:"易县",130634:"曲阳县",130635:"蠡县",130636:"顺平县",130637:"博野县",130638:"雄县",130681:"涿州市",130682:"定州市",130683:"安国市",130684:"高碑店市",130698:"高开区"},130700:{130702:"桥东区",130703:"桥西区",130705:"宣化区",130706:"下花园区",130721:"宣化县",130722:"张北县",130723:"康保县",130724:"沽源县",130725:"尚义县",130726:"蔚县",130727:"阳原县",130728:"怀安县",130729:"万全县",130730:"怀来县",130731:"涿鹿县",130732:"赤城县",130733:"崇礼县"},130800:{130802:"双桥区",130803:"双滦区",130804:"鹰手营子矿区",130821:"承德县",130822:"兴隆县",130823:"平泉县",130824:"滦平县",130825:"隆化县",130826:"丰宁满族自治县",130827:"宽城满族自治县",130828:"围场满族蒙古族自治县"},130900:{130902:"新华区",130903:"运河区",130921:"沧县",130922:"青县",130923:"东光县",130924:"海兴县",130925:"盐山县",130926:"肃宁县",130927:"南皮县",130928:"吴桥县",130929:"献县",130930:"孟村回族自治县",130981:"泊头市",130982:"任丘市",130983:"黄骅市",130984:"河间市"},131e3:{131002:"安次区",131003:"广阳区",131022:"固安县",131023:"永清县",131024:"香河县",131025:"大城县",131026:"文安县",131028:"大厂回族自治县",131051:"开发区",131052:"燕郊经济技术开发区",131081:"霸州市",131082:"三河市",131099:"廊坊经济技术开发区"},131100:{131102:"桃城区",131121:"枣强县",131122:"武邑县",131123:"武强县",131124:"饶阳县",131125:"安平县",131126:"故城县",131127:"景县",131128:"阜城县",131181:"冀州市",131182:"深州市"},14e4:{140100:"太原市",140200:"大同市",140300:"阳泉市",140400:"长治市",140500:"晋城市",140600:"朔州市",140700:"晋中市",140800:"运城市",140900:"忻州市",141e3:"临汾市",141100:"吕梁市"},140100:{140105:"小店区",140106:"迎泽区",140107:"杏花岭区",140108:"尖草坪区",140109:"万柏林区",140110:"晋源区",140121:"清徐县",140122:"阳曲县",140123:"娄烦县",140181:"古交市"},140200:{140202:"城区",140203:"矿区",140211:"南郊区",140212:"新荣区",140221:"阳高县",140222:"天镇县",140223:"广灵县",140224:"灵丘县",140225:"浑源县",140226:"左云县",140227:"大同县"},140300:{140302:"城区",140303:"矿区",140311:"郊区",140321:"平定县",140322:"盂县"},140400:{140421:"长治县",140423:"襄垣县",140424:"屯留县",140425:"平顺县",140426:"黎城县",140427:"壶关县",140428:"长子县",140429:"武乡县",140430:"沁县",140431:"沁源县",140481:"潞城市",140482:"城区",140483:"郊区",140484:"高新区"},140500:{140502:"城区",140521:"沁水县",140522:"阳城县",140524:"陵川县",140525:"泽州县",140581:"高平市"},140600:{140602:"朔城区",140603:"平鲁区",140621:"山阴县",140622:"应县",140623:"右玉县",140624:"怀仁县"},140700:{140702:"榆次区",140721:"榆社县",140722:"左权县",140723:"和顺县",140724:"昔阳县",140725:"寿阳县",140726:"太谷县",140727:"祁县",140728:"平遥县",140729:"灵石县",140781:"介休市"},140800:{140802:"盐湖区",140803:"风陵渡经济开发区",140821:"临猗县",140822:"万荣县",140823:"闻喜县",140824:"稷山县",140825:"新绛县",140826:"绛县",140827:"垣曲县",140828:"夏县",140829:"平陆县",140830:"芮城县",140881:"永济市",140882:"河津市"},140900:{140902:"忻府区",140921:"定襄县",140922:"五台县",140923:"代县",140924:"繁峙县",140925:"宁武县",140926:"静乐县",140927:"神池县",140928:"五寨县",140929:"岢岚县",140930:"河曲县",140931:"保德县",140932:"偏关县",140981:"原平市"},141e3:{141002:"尧都区",141021:"曲沃县",141022:"翼城县",141023:"襄汾县",141024:"洪洞县",141025:"古县",141026:"安泽县",141027:"浮山县",141028:"吉县",141029:"乡宁县",141030:"大宁县",141031:"隰县",141032:"永和县",141033:"蒲县",141034:"汾西县",141081:"侯马市",141082:"霍州市"},141100:{141102:"离石区",141121:"文水县",141122:"交城县",141123:"兴县",141124:"临县",141125:"柳林县",141126:"石楼县",141127:"岚县",141128:"方山县",141129:"中阳县",141130:"交口县",141181:"孝义市",141182:"汾阳市"},15e4:{150100:"呼和浩特市",150200:"包头市",150300:"乌海市",150400:"赤峰市",150500:"通辽市",150600:"鄂尔多斯市",150700:"呼伦贝尔市",150800:"巴彦淖尔市",150900:"乌兰察布市",152200:"兴安盟",152500:"锡林郭勒盟",152900:"阿拉善盟"},150100:{150102:"新城区",150103:"回民区",150104:"玉泉区",150105:"赛罕区",150121:"土默特左旗",150122:"托克托县",150123:"和林格尔县",150124:"清水河县",150125:"武川县"},150200:{150202:"东河区",150203:"昆都仑区",150204:"青山区",150205:"石拐区",150206:"白云鄂博矿区",150207:"九原区",150221:"土默特右旗",150222:"固阳县",150223:"达尔罕茂明安联合旗"},150300:{150302:"海勃湾区",150303:"海南区",150304:"乌达区"},150400:{150402:"红山区",150403:"元宝山区",150404:"松山区",150421:"阿鲁科尔沁旗",150422:"巴林左旗",150423:"巴林右旗",150424:"林西县",150425:"克什克腾旗",150426:"翁牛特旗",150428:"喀喇沁旗",150429:"宁城县",150430:"敖汉旗"},150500:{150502:"科尔沁区",150521:"科尔沁左翼中旗",150522:"科尔沁左翼后旗",150523:"开鲁县",150524:"库伦旗",150525:"奈曼旗",150526:"扎鲁特旗",150581:"霍林郭勒市"},150600:{150602:"东胜区",150621:"达拉特旗",150622:"准格尔旗",150623:"鄂托克前旗",150624:"鄂托克旗",150625:"杭锦旗",150626:"乌审旗",150627:"伊金霍洛旗"},150700:{150702:"海拉尔区",150703:"扎赉诺尔区",150721:"阿荣旗",150722:"莫力达瓦达斡尔族自治旗",150723:"鄂伦春自治旗",150724:"鄂温克族自治旗",150725:"陈巴尔虎旗",150726:"新巴尔虎左旗",150727:"新巴尔虎右旗",150781:"满洲里市",150782:"牙克石市",150783:"扎兰屯市",150784:"额尔古纳市",150785:"根河市"},150800:{150802:"临河区",150821:"五原县",150822:"磴口县",150823:"乌拉特前旗",150824:"乌拉特中旗",150825:"乌拉特后旗",150826:"杭锦后旗"},150900:{150902:"集宁区",150921:"卓资县",150922:"化德县",150923:"商都县",150924:"兴和县",150925:"凉城县",150926:"察哈尔右翼前旗",150927:"察哈尔右翼中旗",150928:"察哈尔右翼后旗",150929:"四子王旗",150981:"丰镇市"},152200:{152201:"乌兰浩特市",152202:"阿尔山市",152221:"科尔沁右翼前旗",152222:"科尔沁右翼中旗",152223:"扎赉特旗",152224:"突泉县"},152500:{152501:"二连浩特市",152502:"锡林浩特市",152522:"阿巴嘎旗",152523:"苏尼特左旗",152524:"苏尼特右旗",152525:"东乌珠穆沁旗",152526:"西乌珠穆沁旗",152527:"太仆寺旗",152528:"镶黄旗",152529:"正镶白旗",152530:"正蓝旗",152531:"多伦县"},152900:{152921:"阿拉善左旗",152922:"阿拉善右旗",152923:"额济纳旗"},21e4:{210100:"沈阳市",210200:"大连市",210300:"鞍山市",210400:"抚顺市",210500:"本溪市",210600:"丹东市",210700:"锦州市",210800:"营口市",210900:"阜新市",211e3:"辽阳市",211100:"盘锦市",211200:"铁岭市",211300:"朝阳市",211400:"葫芦岛市"},210100:{210102:"和平区",210103:"沈河区",210104:"大东区",210105:"皇姑区",210106:"铁西区",210111:"苏家屯区",210112:"东陵区",210113:"新城子区",210114:"于洪区",210122:"辽中县",210123:"康平县",210124:"法库县",210181:"新民市",210182:"浑南区",210183:"张士开发区",210184:"沈北新区"},210200:{210202:"中山区",210203:"西岗区",210204:"沙河口区",210211:"甘井子区",210212:"旅顺口区",210213:"金州区",210214:"大连经济技术开发区",210224:"长海县",210251:"开发区",210281:"瓦房店市",210282:"普兰店市",210283:"庄河市",210297:"岭前区"},210300:{210302:"铁东区",210303:"铁西区",210304:"立山区",210311:"千山区",210321:"台安县",210323:"岫岩满族自治县",210351:"高新区",210381:"海城市"},210400:{210402:"新抚区",210403:"东洲区",210404:"望花区",210411:"顺城区",210421:"抚顺县",210422:"新宾满族自治县",210423:"清原满族自治县"},210500:{210502:"平山区",210503:"溪湖区",210504:"明山区",210505:"南芬区",210521:"本溪满族自治县",210522:"桓仁满族自治县"},210600:{210602:"元宝区",210603:"振兴区",210604:"振安区",210624:"宽甸满族自治县",210681:"东港市",210682:"凤城市"},210700:{210702:"古塔区",210703:"凌河区",210711:"太和区",210726:"黑山县",210727:"义县",210781:"凌海市",210782:"北镇市"},210800:{210802:"站前区",210803:"西市区",210804:"鲅鱼圈区",210811:"老边区",210881:"盖州市",210882:"大石桥市"},210900:{210902:"海州区",210903:"新邱区",210904:"太平区",210905:"清河门区",210911:"细河区",210921:"阜新蒙古族自治县",210922:"彰武县"},211e3:{211002:"白塔区",211003:"文圣区",211004:"宏伟区",211005:"弓长岭区",211011:"太子河区",211021:"辽阳县",211081:"灯塔市"},211100:{211102:"双台子区",211103:"兴隆台区",211121:"大洼县",211122:"盘山县"},211200:{211202:"银州区",211204:"清河区",211221:"铁岭县",211223:"西丰县",211224:"昌图县",211281:"调兵山市",211282:"开原市"},211300:{211302:"双塔区",211303:"龙城区",211321:"朝阳县",211322:"建平县",211324:"喀喇沁左翼蒙古族自治县",211381:"北票市",211382:"凌源市"},211400:{211402:"连山区",211403:"龙港区",211404:"南票区",211421:"绥中县",211422:"建昌县",211481:"兴城市"},22e4:{220100:"长春市",220200:"吉林市",220300:"四平市",220400:"辽源市",220500:"通化市",220600:"白山市",220700:"松原市",220800:"白城市",222400:"延边朝鲜族自治州"},220100:{220102:"南关区",220103:"宽城区",220104:"朝阳区",220105:"二道区",220106:"绿园区",220112:"双阳区",220117:"高新北区",220119:"高新区",220122:"农安县",220181:"九台区",220182:"榆树市",220183:"德惠市",220184:"高新技术产业开发区",220185:"汽车产业开发区",220186:"经济技术开发区",220187:"净月旅游开发区"},220200:{220202:"昌邑区",220203:"龙潭区",220204:"船营区",220211:"丰满区",220221:"永吉县",220281:"蛟河市",220282:"桦甸市",220283:"舒兰市",220284:"磐石市"},220300:{220302:"铁西区",220303:"铁东区",220322:"梨树县",220323:"伊通满族自治县",220381:"公主岭市",220382:"双辽市"},220400:{220402:"龙山区",220403:"西安区",220421:"东丰县",220422:"东辽县"},220500:{220502:"东昌区",220503:"二道江区",220521:"通化县",220523:"辉南县",220524:"柳河县",220581:"梅河口市",220582:"集安市"},220600:{220602:"浑江区",220603:"八道江区",220621:"抚松县",220622:"靖宇县",220623:"长白朝鲜族自治县",220625:"江源区",220681:"临江市"},220700:{220702:"宁江区",220721:"前郭尔罗斯蒙古族自治县",220722:"长岭县",220723:"乾安县",220781:"扶余市"},220800:{220802:"洮北区",220821:"镇赉县",220822:"通榆县",220881:"洮南市",220882:"大安市"},222400:{222401:"延吉市",222402:"图们市",222403:"敦化市",222404:"珲春市",222405:"龙井市",222406:"和龙市",222424:"汪清县",222426:"安图县"},23e4:{230100:"哈尔滨市",230200:"齐齐哈尔市",230300:"鸡西市",230400:"鹤岗市",230500:"双鸭山市",230600:"大庆市",230700:"伊春市",230800:"佳木斯市",230900:"七台河市",231e3:"牡丹江市",231100:"黑河市",231200:"绥化市",232700:"大兴安岭地区"},230100:{230102:"道里区",230103:"南岗区",230104:"道外区",230106:"香坊区",230107:"动力区",230108:"平房区",230109:"松北区",230110:"香坊区",230111:"呼兰区",230123:"依兰县",230124:"方正县",230125:"宾县",230126:"巴彦县",230127:"木兰县",230128:"通河县",230129:"延寿县",230181:"阿城区",230182:"双城区",230183:"尚志市",230184:"五常市",230185:"阿城市"},230200:{230202:"龙沙区",230203:"建华区",230204:"铁锋区",230205:"昂昂溪区",230206:"富拉尔基区",230207:"碾子山区",230208:"梅里斯达斡尔族区",230221:"龙江县",230223:"依安县",230224:"泰来县",230225:"甘南县",230227:"富裕县",230229:"克山县",230230:"克东县",230231:"拜泉县",230281:"讷河市"},230300:{230302:"鸡冠区",230303:"恒山区",230304:"滴道区",230305:"梨树区",230306:"城子河区",230307:"麻山区",230321:"鸡东县",230381:"虎林市",230382:"密山市"},230400:{230402:"向阳区",230403:"工农区",230404:"南山区",230405:"兴安区",230406:"东山区",230407:"兴山区",230421:"萝北县",230422:"绥滨县"},230500:{230502:"尖山区",230503:"岭东区",230505:"四方台区",230506:"宝山区",230521:"集贤县",230522:"友谊县",230523:"宝清县",230524:"饶河县"},230600:{230602:"萨尔图区",230603:"龙凤区",230604:"让胡路区",230605:"红岗区",230606:"大同区",230621:"肇州县",230622:"肇源县",230623:"林甸县",230624:"杜尔伯特蒙古族自治县"},230700:{230702:"伊春区",230703:"南岔区",230704:"友好区",230705:"西林区",230706:"翠峦区",230707:"新青区",230708:"美溪区",230709:"金山屯区",230710:"五营区",230711:"乌马河区",230712:"汤旺河区",230713:"带岭区",230714:"乌伊岭区",230715:"红星区",230716:"上甘岭区",230722:"嘉荫县",230781:"铁力市"},230800:{230802:"永红区",230803:"向阳区",230804:"前进区",230805:"东风区",230811:"郊区",230822:"桦南县",230826:"桦川县",230828:"汤原县",230833:"抚远县",230881:"同江市",230882:"富锦市"},230900:{230902:"新兴区",230903:"桃山区",230904:"茄子河区",230921:"勃利县"},231e3:{231002:"东安区",231003:"阳明区",231004:"爱民区",231005:"西安区",231024:"东宁县",231025:"林口县",231081:"绥芬河市",231083:"海林市",231084:"宁安市",231085:"穆棱市"},231100:{231102:"爱辉区",231121:"嫩江县",231123:"逊克县",231124:"孙吴县",231181:"北安市",231182:"五大连池市"},231200:{231202:"北林区",231221:"望奎县",231222:"兰西县",231223:"青冈县",231224:"庆安县",231225:"明水县",231226:"绥棱县",231281:"安达市",231282:"肇东市",231283:"海伦市"},232700:{232702:"松岭区",232703:"新林区",232704:"呼中区",232721:"呼玛县",232722:"塔河县",232723:"漠河县",232724:"加格达奇区"},31e4:{310100:"上海市"},310100:{310101:"黄浦区",310103:"卢湾区",310104:"徐汇区",310105:"长宁区",310106:"静安区",310107:"普陀区",310108:"闸北区",310109:"虹口区",310110:"杨浦区",310112:"闵行区",310113:"宝山区",310114:"嘉定区",310115:"浦东新区",310116:"金山区",310117:"松江区",310118:"青浦区",310119:"南汇区",310120:"奉贤区",310152:"川沙区",310230:"崇明县"},32e4:{320100:"南京市",320200:"无锡市",320300:"徐州市",320400:"常州市",320500:"苏州市",320600:"南通市",320700:"连云港市",320800:"淮安市",320900:"盐城市",321e3:"扬州市",321100:"镇江市",321200:"泰州市",321300:"宿迁市"},320100:{320102:"玄武区",320103:"白下区",320104:"秦淮区",320105:"建邺区",320106:"鼓楼区",320107:"下关区",320111:"浦口区",320113:"栖霞区",320114:"雨花台区",320115:"江宁区",320116:"六合区",320124:"溧水区",320125:"高淳区"},320200:{320202:"崇安区",320203:"南长区",320204:"北塘区",320205:"锡山区",320206:"惠山区",320211:"滨湖区",320213:"梁溪区",320214:"新吴区",320281:"江阴市",320282:"宜兴市",320296:"新区"},320300:{320302:"鼓楼区",320303:"云龙区",320304:"九里区",320305:"贾汪区",320311:"泉山区",320321:"丰县",320322:"沛县",320323:"铜山区",320324:"睢宁县",320381:"新沂市",320382:"邳州市"},320400:{320402:"天宁区",320404:"钟楼区",320405:"戚墅堰区",320411:"新北区",320412:"武进区",320481:"溧阳市",320482:"金坛区"},320500:{320502:"沧浪区",320503:"平江区",320504:"金阊区",320505:"虎丘区",320506:"吴中区",320507:"相城区",320508:"姑苏区",320510:"新区",320581:"常熟市",320582:"张家港市",320583:"昆山市",320584:"吴江区",320585:"太仓市",320595:"园区"},320600:{320602:"崇川区",320611:"港闸区",320612:"通州区",320613:"开发区",320621:"海安县",320623:"如东县",320681:"启东市",320682:"如皋市",320683:"通州市",320684:"海门市",320693:"开发区"},320700:{320703:"连云区",320705:"新浦区",320706:"海州区",320721:"赣榆区",320722:"东海县",320723:"灌云县",320724:"灌南县"},320800:{320802:"清河区",320803:"淮安区",320804:"淮阴区",320809:"开发区",320811:"清浦区",320826:"涟水县",320829:"洪泽县",320830:"盱眙县",320831:"金湖县"},320900:{320902:"亭湖区",320903:"盐都区",320921:"响水县",320922:"滨海县",320923:"阜宁县",320924:"射阳县",320925:"建湖县",320981:"东台市",320982:"大丰市"},321e3:{321002:"广陵区",321003:"邗江区",321011:"维扬区",321023:"宝应县",321081:"仪征市",321084:"高邮市",321088:"江都区",321092:"经济开发区"},321100:{321102:"京口区",321111:"润州区",321112:"丹徒区",321181:"丹阳市",321182:"扬中市",321183:"句容市"},321200:{321202:"海陵区",321203:"高港区",321281:"兴化市",321282:"靖江市",321283:"泰兴市",321284:"姜堰区"},321300:{321302:"宿城区",321311:"宿豫区",321322:"沭阳县",321323:"泗阳县",321324:"泗洪县"},33e4:{330100:"杭州市",330200:"宁波市",330300:"温州市",330400:"嘉兴市",330500:"湖州市",330600:"绍兴市",330700:"金华市",330800:"衢州市",330900:"舟山市",331e3:"台州市",331100:"丽水市"},330100:{330102:"上城区",330103:"下城区",330104:"江干区",330105:"拱墅区",330106:"西湖区",330108:"滨江区",330109:"萧山区",330110:"余杭区",330122:"桐庐县",330127:"淳安县",330182:"建德市",330183:"富阳区",330185:"临安市"},330200:{330203:"海曙区",330204:"江东区",330205:"江北区",330206:"北仑区",330211:"镇海区",330212:"鄞州区",330225:"象山县",330226:"宁海县",330281:"余姚市",330282:"慈溪市",330283:"奉化市",330299:"高新区"},330300:{330302:"鹿城区",330303:"龙湾区",330304:"瓯海区",330322:"洞头县",330324:"永嘉县",330326:"平阳县",330327:"苍南县",330328:"文成县",330329:"泰顺县",330381:"瑞安市",330382:"乐清市"},330400:{330402:"南湖区",330411:"秀洲区",330421:"嘉善县",330424:"海盐县",330481:"海宁市",330482:"平湖市",330483:"桐乡市"},330500:{330502:"吴兴区",330503:"南浔区",330521:"德清县",330522:"长兴县",330523:"安吉县"},330600:{330602:"越城区",330621:"柯桥区",330624:"新昌县",330681:"诸暨市",330682:"上虞区",330683:"嵊州市"},330700:{330702:"婺城区",330703:"金东区",330723:"武义县",330726:"浦江县",330727:"磐安县",330781:"兰溪市",330782:"义乌市",330783:"东阳市",330784:"永康市"},330800:{330802:"柯城区",330803:"衢江区",330822:"常山县",330824:"开化县",330825:"龙游县",330881:"江山市"},330900:{330902:"定海区",330903:"普陀区",330921:"岱山县",330922:"嵊泗县"},331e3:{331002:"椒江区",331003:"黄岩区",331004:"路桥区",331021:"玉环县",331022:"三门县",331023:"天台县",331024:"仙居县",331081:"温岭市",331082:"临海市"},331100:{331102:"莲都区",331121:"青田县",331122:"缙云县",331123:"遂昌县",331124:"松阳县",331125:"云和县",331126:"庆元县",331127:"景宁畲族自治县",331181:"龙泉市"},34e4:{340100:"合肥市",340200:"芜湖市",340300:"蚌埠市",340400:"淮南市",340500:"马鞍山市",340600:"淮北市",340700:"铜陵市",340800:"安庆市",341e3:"黄山市",341100:"滁州市",341200:"阜阳市",341300:"宿州市",341500:"六安市",341600:"亳州市",341700:"池州市",341800:"宣城市"},340100:{340102:"瑶海区",340103:"庐阳区",340104:"蜀山区",340111:"包河区",340121:"长丰县",340122:"肥东县",340123:"肥西县",340151:"高新区",340191:"中区",340198:"合肥经济技术开发区",340199:"合肥市政务新区",341400:"巢湖市",341402:"居巢区",341421:"庐江县"},340200:{340202:"镜湖区",340203:"弋江区",340207:"鸠江区",340208:"三山区",340221:"芜湖县",340222:"繁昌县",340223:"南陵县",341422:"无为县"},340300:{340302:"龙子湖区",340303:"蚌山区",340304:"禹会区",340311:"淮上区",340321:"怀远县",340322:"五河县",340323:"固镇县"},340400:{340402:"大通区",340403:"田家庵区",340404:"谢家集区",340405:"八公山区", +340406:"潘集区",340421:"凤台县",341521:"寿县"},340500:{340502:"金家庄区",340503:"花山区",340504:"雨山区",340506:"博望区",340521:"当涂县",341423:"含山县",341424:"和县"},340600:{340602:"杜集区",340603:"相山区",340604:"烈山区",340621:"濉溪县"},340700:{340702:"铜官山区",340703:"狮子山区",340711:"郊区",340721:"义安区",340823:"枞阳县"},340800:{340802:"迎江区",340803:"大观区",340811:"宜秀区",340822:"怀宁县",340823:"枞阳县",340824:"潜山县",340825:"太湖县",340826:"宿松县",340827:"望江县",340828:"岳西县",340881:"桐城市"},341e3:{341002:"屯溪区",341003:"黄山区",341004:"徽州区",341021:"歙县",341022:"休宁县",341023:"黟县",341024:"祁门县"},341100:{341102:"琅琊区",341103:"南谯区",341122:"来安县",341124:"全椒县",341125:"定远县",341126:"凤阳县",341181:"天长市",341182:"明光市"},341200:{341202:"颍州区",341203:"颍东区",341204:"颍泉区",341221:"临泉县",341222:"太和县",341225:"阜南县",341226:"颍上县",341282:"界首市"},341300:{341302:"埇桥区",341321:"砀山县",341322:"萧县",341323:"灵璧县",341324:"泗县"},341500:{341502:"金安区",341503:"裕安区",341522:"霍邱县",341523:"舒城县",341524:"金寨县",341525:"霍山县",341526:"叶集区"},341600:{341602:"谯城区",341621:"涡阳县",341622:"蒙城县",341623:"利辛县"},341700:{341702:"贵池区",341721:"东至县",341722:"石台县",341723:"青阳县"},341800:{341802:"宣州区",341821:"郎溪县",341822:"广德县",341823:"泾县",341824:"绩溪县",341825:"旌德县",341881:"宁国市"},35e4:{350100:"福州市",350200:"厦门市",350300:"莆田市",350400:"三明市",350500:"泉州市",350600:"漳州市",350700:"南平市",350800:"龙岩市",350900:"宁德市"},350100:{350102:"鼓楼区",350103:"台江区",350104:"仓山区",350105:"马尾区",350111:"晋安区",350121:"闽侯县",350122:"连江县",350123:"罗源县",350124:"闽清县",350125:"永泰县",350128:"平潭县",350181:"福清市",350182:"长乐市"},350200:{350203:"思明区",350205:"海沧区",350206:"湖里区",350211:"集美区",350212:"同安区",350213:"翔安区"},350300:{350302:"城厢区",350303:"涵江区",350304:"荔城区",350305:"秀屿区",350322:"仙游县"},350400:{350402:"梅列区",350403:"三元区",350421:"明溪县",350423:"清流县",350424:"宁化县",350425:"大田县",350426:"尤溪县",350427:"沙县",350428:"将乐县",350429:"泰宁县",350430:"建宁县",350481:"永安市"},350500:{350502:"鲤城区",350503:"丰泽区",350504:"洛江区",350505:"泉港区",350521:"惠安县",350524:"安溪县",350525:"永春县",350526:"德化县",350527:"金门县",350581:"石狮市",350582:"晋江市",350583:"南安市"},350600:{350602:"芗城区",350603:"龙文区",350622:"云霄县",350623:"漳浦县",350624:"诏安县",350625:"长泰县",350626:"东山县",350627:"南靖县",350628:"平和县",350629:"华安县",350681:"龙海市"},350700:{350702:"延平区",350721:"顺昌县",350722:"浦城县",350723:"光泽县",350724:"松溪县",350725:"政和县",350781:"邵武市",350782:"武夷山市",350783:"建瓯市",350784:"建阳区"},350800:{350802:"新罗区",350821:"长汀县",350822:"永定区",350823:"上杭县",350824:"武平县",350825:"连城县",350881:"漳平市"},350900:{350902:"蕉城区",350921:"霞浦县",350922:"古田县",350923:"屏南县",350924:"寿宁县",350925:"周宁县",350926:"柘荣县",350981:"福安市",350982:"福鼎市"},36e4:{360100:"南昌市",360200:"景德镇市",360300:"萍乡市",360400:"九江市",360500:"新余市",360600:"鹰潭市",360700:"赣州市",360800:"吉安市",360900:"宜春市",361e3:"抚州市",361100:"上饶市"},360100:{360102:"东湖区",360103:"西湖区",360104:"青云谱区",360105:"湾里区",360111:"青山湖区",360121:"南昌县",360122:"新建县",360123:"安义县",360124:"进贤县",360125:"红谷滩新区",360126:"经济技术开发区",360127:"昌北区"},360200:{360202:"昌江区",360203:"珠山区",360222:"浮梁县",360281:"乐平市"},360300:{360302:"安源区",360313:"湘东区",360321:"莲花县",360322:"上栗县",360323:"芦溪县"},360400:{360402:"庐山区",360403:"浔阳区",360421:"九江县",360423:"武宁县",360424:"修水县",360425:"永修县",360426:"德安县",360427:"星子县",360428:"都昌县",360429:"湖口县",360430:"彭泽县",360481:"瑞昌市",360483:"共青城市"},360500:{360502:"渝水区",360521:"分宜县"},360600:{360602:"月湖区",360622:"余江县",360681:"贵溪市"},360700:{360702:"章贡区",360704:"赣县",360722:"信丰县",360723:"大余县",360724:"上犹县",360725:"崇义县",360726:"安远县",360727:"龙南县",360728:"定南县",360729:"全南县",360730:"宁都县",360731:"于都县",360732:"兴国县",360733:"会昌县",360734:"寻乌县",360735:"石城县",360751:"黄金区",360781:"瑞金市",360782:"南康区"},360800:{360802:"吉州区",360803:"青原区",360821:"吉安县",360822:"吉水县",360823:"峡江县",360824:"新干县",360825:"永丰县",360826:"泰和县",360827:"遂川县",360828:"万安县",360829:"安福县",360830:"永新县",360881:"井冈山市"},360900:{360902:"袁州区",360921:"奉新县",360922:"万载县",360923:"上高县",360924:"宜丰县",360925:"靖安县",360926:"铜鼓县",360981:"丰城市",360982:"樟树市",360983:"高安市"},361e3:{361002:"临川区",361021:"南城县",361022:"黎川县",361023:"南丰县",361024:"崇仁县",361025:"乐安县",361026:"宜黄县",361027:"金溪县",361028:"资溪县",361029:"东乡县",361030:"广昌县"},361100:{361102:"信州区",361121:"上饶县",361122:"广丰县",361123:"玉山县",361124:"铅山县",361125:"横峰县",361126:"弋阳县",361127:"余干县",361128:"鄱阳县",361129:"万年县",361130:"婺源县",361181:"德兴市"},37e4:{370100:"济南市",370200:"青岛市",370300:"淄博市",370400:"枣庄市",370500:"东营市",370600:"烟台市",370700:"潍坊市",370800:"济宁市",370900:"泰安市",371e3:"威海市",371100:"日照市",371200:"莱芜市",371300:"临沂市",371400:"德州市",371500:"聊城市",371600:"滨州市",371700:"菏泽市"},370100:{250101:"高新区",370102:"历下区",370103:"市中区",370104:"槐荫区",370105:"天桥区",370112:"历城区",370113:"长清区",370124:"平阴县",370125:"济阳县",370126:"商河县",370181:"章丘市"},370200:{370202:"市南区",370203:"市北区",370204:"西海岸新区",370205:"四方区",370211:"黄岛区",370212:"崂山区",370213:"李沧区",370214:"城阳区",370251:"开发区",370281:"胶州市",370282:"即墨市",370283:"平度市",370284:"胶南市",370285:"莱西市"},370300:{370302:"淄川区",370303:"张店区",370304:"博山区",370305:"临淄区",370306:"周村区",370321:"桓台县",370322:"高青县",370323:"沂源县"},370400:{370402:"市中区",370403:"薛城区",370404:"峄城区",370405:"台儿庄区",370406:"山亭区",370481:"滕州市"},370500:{370502:"东营区",370503:"河口区",370521:"垦利县",370522:"利津县",370523:"广饶县",370589:"西城区",370590:"东城区"},370600:{370602:"芝罘区",370611:"福山区",370612:"牟平区",370613:"莱山区",370634:"长岛县",370681:"龙口市",370682:"莱阳市",370683:"莱州市",370684:"蓬莱市",370685:"招远市",370686:"栖霞市",370687:"海阳市"},370700:{370702:"潍城区",370703:"寒亭区",370704:"坊子区",370705:"奎文区",370706:"滨海经济技术开发区",370724:"临朐县",370725:"昌乐县",370751:"开发区",370781:"青州市",370782:"诸城市",370783:"寿光市",370784:"安丘市",370785:"高密市",370786:"昌邑市"},370800:{370802:"市中区",370811:"任城区",370826:"微山县",370827:"鱼台县",370828:"金乡县",370829:"嘉祥县",370830:"汶上县",370831:"泗水县",370832:"梁山县",370881:"曲阜市",370882:"兖州区",370883:"邹城市"},370900:{370902:"泰山区",370903:"岱岳区",370921:"宁阳县",370923:"东平县",370982:"新泰市",370983:"肥城市"},371e3:{371002:"环翠区",371081:"文登区",371082:"荣成市",371083:"乳山市"},371100:{371102:"东港区",371103:"岚山区",371121:"五莲县",371122:"莒县"},371200:{371202:"莱城区",371203:"钢城区"},371300:{371302:"兰山区",371311:"罗庄区",371312:"河东区",371321:"沂南县",371322:"郯城县",371323:"沂水县",371324:"兰陵县",371325:"费县",371326:"平邑县",371327:"莒南县",371328:"蒙阴县",371329:"临沭县"},371400:{371402:"德城区",371421:"陵城区",371422:"宁津县",371423:"庆云县",371424:"临邑县",371425:"齐河县",371426:"平原县",371427:"夏津县",371428:"武城县",371451:"开发区",371481:"乐陵市",371482:"禹城市"},371500:{371502:"东昌府区",371521:"阳谷县",371522:"莘县",371523:"茌平县",371524:"东阿县",371525:"冠县",371526:"高唐县",371581:"临清市"},371600:{371602:"滨城区",371621:"惠民县",371622:"阳信县",371623:"无棣县",371624:"沾化区",371625:"博兴县",371626:"邹平县"},371700:{371702:"牡丹区",371721:"曹县",371722:"单县",371723:"成武县",371724:"巨野县",371725:"郓城县",371726:"鄄城县",371727:"定陶县",371728:"东明县"},41e4:{410100:"郑州市",410200:"开封市",410300:"洛阳市",410400:"平顶山市",410500:"安阳市",410600:"鹤壁市",410700:"新乡市",410800:"焦作市",410900:"濮阳市",411e3:"许昌市",411100:"漯河市",411200:"三门峡市",411300:"南阳市",411400:"商丘市",411500:"信阳市",411600:"周口市",411700:"驻马店市",419001:"济源市"},410100:{410102:"中原区",410103:"二七区",410104:"管城回族区",410105:"金水区",410106:"上街区",410108:"惠济区",410122:"中牟县",410181:"巩义市",410182:"荥阳市",410183:"新密市",410184:"新郑市",410185:"登封市",410186:"郑东新区",410187:"高新区",410193:"经济开发区"},410200:{410202:"龙亭区",410203:"顺河回族区",410204:"鼓楼区",410205:"禹王台区",410211:"金明区",410221:"杞县",410222:"通许县",410223:"尉氏县",410224:"祥符区",410225:"兰考县"},410300:{410302:"老城区",410303:"西工区",410304:"瀍河回族区",410305:"涧西区",410306:"吉利区",410307:"洛龙区",410322:"孟津县",410323:"新安县",410324:"栾川县",410325:"嵩县",410326:"汝阳县",410327:"宜阳县",410328:"洛宁县",410329:"伊川县",410381:"偃师市"},410400:{410402:"新华区",410403:"卫东区",410404:"石龙区",410411:"湛河区",410421:"宝丰县",410422:"叶县",410423:"鲁山县",410425:"郏县",410481:"舞钢市",410482:"汝州市"},410500:{410502:"文峰区",410503:"北关区",410505:"殷都区",410506:"龙安区",410522:"安阳县",410523:"汤阴县",410526:"滑县",410527:"内黄县",410581:"林州市"},410600:{410602:"鹤山区",410603:"山城区",410611:"淇滨区",410621:"浚县",410622:"淇县"},410700:{410702:"红旗区",410703:"卫滨区",410704:"凤泉区",410711:"牧野区",410721:"新乡县",410724:"获嘉县",410725:"原阳县",410726:"延津县",410727:"封丘县",410728:"长垣县",410781:"卫辉市",410782:"辉县市"},410800:{410802:"解放区",410803:"中站区",410804:"马村区",410811:"山阳区",410821:"修武县",410822:"博爱县",410823:"武陟县",410825:"温县",410882:"沁阳市",410883:"孟州市"},410900:{410902:"华龙区",410922:"清丰县",410923:"南乐县",410926:"范县",410927:"台前县",410928:"濮阳县"},411e3:{411002:"魏都区",411023:"许昌县",411024:"鄢陵县",411025:"襄城县",411081:"禹州市",411082:"长葛市"},411100:{411102:"源汇区",411103:"郾城区",411104:"召陵区",411121:"舞阳县",411122:"临颍县"},411200:{411202:"湖滨区",411221:"渑池县",411222:"陕州区",411224:"卢氏县",411281:"义马市",411282:"灵宝市"},411300:{411302:"宛城区",411303:"卧龙区",411321:"南召县",411322:"方城县",411323:"西峡县",411324:"镇平县",411325:"内乡县",411326:"淅川县",411327:"社旗县",411328:"唐河县",411329:"新野县",411330:"桐柏县",411381:"邓州市"},411400:{411402:"梁园区",411403:"睢阳区",411421:"民权县",411422:"睢县",411423:"宁陵县",411424:"柘城县",411425:"虞城县",411426:"夏邑县",411481:"永城市"},411500:{411502:"浉河区",411503:"平桥区",411521:"罗山县",411522:"光山县",411523:"新县",411524:"商城县",411525:"固始县",411526:"潢川县",411527:"淮滨县",411528:"息县"},411600:{411602:"川汇区",411621:"扶沟县",411622:"西华县",411623:"商水县",411624:"沈丘县",411625:"郸城县",411626:"淮阳县",411627:"太康县",411628:"鹿邑县",411681:"项城市"},411700:{411702:"驿城区",411721:"西平县",411722:"上蔡县",411723:"平舆县",411724:"正阳县",411725:"确山县",411726:"泌阳县",411727:"汝南县",411728:"遂平县",411729:"新蔡县"},42e4:{420100:"武汉市",420200:"黄石市",420300:"十堰市",420500:"宜昌市",420600:"襄阳市",420700:"鄂州市",420800:"荆门市",420900:"孝感市",421e3:"荆州市",421100:"黄冈市",421200:"咸宁市",421300:"随州市",422800:"恩施土家族苗族自治州",422900:"仙桃市",429005:"潜江市",429006:"天门市",429021:"神农架林区"},420100:{420102:"江岸区",420103:"江汉区",420104:"硚口区",420105:"汉阳区",420106:"武昌区",420107:"青山区",420111:"洪山区",420112:"东西湖区",420113:"汉南区",420114:"蔡甸区",420115:"江夏区",420116:"黄陂区",420117:"新洲区",420199:"武汉经济技术开发区"},420200:{420202:"黄石港区",420203:"西塞山区",420204:"下陆区",420205:"铁山区",420222:"阳新县",420281:"大冶市"},420300:{420302:"茅箭区",420303:"张湾区",420321:"郧阳区",420322:"郧西县",420323:"竹山县",420324:"竹溪县",420325:"房县",420381:"丹江口市",420382:"城区"},420500:{420502:"西陵区",420503:"伍家岗区",420504:"点军区",420505:"猇亭区",420506:"夷陵区",420525:"远安县",420526:"兴山县",420527:"秭归县",420528:"长阳土家族自治县",420529:"五峰土家族自治县",420551:"葛洲坝区",420552:"开发区",420581:"宜都市",420582:"当阳市",420583:"枝江市"},420600:{420602:"襄城区",420606:"樊城区",420607:"襄州区",420624:"南漳县",420625:"谷城县",420626:"保康县",420682:"老河口市",420683:"枣阳市",420684:"宜城市"},420700:{420702:"梁子湖区",420703:"华容区",420704:"鄂城区"},420800:{420802:"东宝区",420804:"掇刀区",420821:"京山县",420822:"沙洋县",420881:"钟祥市"},420900:{420902:"孝南区",420921:"孝昌县",420922:"大悟县",420923:"云梦县",420981:"应城市",420982:"安陆市",420984:"汉川市"},421e3:{421002:"沙市区",421003:"荆州区",421022:"公安县",421023:"监利县",421024:"江陵县",421081:"石首市",421083:"洪湖市",421087:"松滋市"},421100:{421102:"黄州区",421121:"团风县",421122:"红安县",421123:"罗田县",421124:"英山县",421125:"浠水县",421126:"蕲春县",421127:"黄梅县",421181:"麻城市",421182:"武穴市"},421200:{421202:"咸安区",421221:"嘉鱼县",421222:"通城县",421223:"崇阳县",421224:"通山县",421281:"赤壁市",421282:"温泉城区"},421300:{421303:"曾都区",421321:"随县",421381:"广水市"},422800:{422801:"恩施市",422802:"利川市",422822:"建始县",422823:"巴东县",422825:"宣恩县",422826:"咸丰县",422827:"来凤县",422828:"鹤峰县"},43e4:{430100:"长沙市",430200:"株洲市",430300:"湘潭市",430400:"衡阳市",430500:"邵阳市",430600:"岳阳市",430700:"常德市",430800:"张家界市",430900:"益阳市",431e3:"郴州市",431100:"永州市",431200:"怀化市",431300:"娄底市",433100:"湘西土家族苗族自治州"},430100:{430102:"芙蓉区",430103:"天心区",430104:"岳麓区",430105:"开福区",430111:"雨花区",430121:"长沙县",430122:"望城区",430124:"宁乡县",430181:"浏阳市"},430200:{430202:"荷塘区",430203:"芦淞区",430204:"石峰区",430211:"天元区",430221:"株洲县",430223:"攸县",430224:"茶陵县",430225:"炎陵县",430281:"醴陵市"},430300:{430302:"雨湖区",430304:"岳塘区",430321:"湘潭县",430381:"湘乡市",430382:"韶山市"},430400:{430405:"珠晖区",430406:"雁峰区",430407:"石鼓区",430408:"蒸湘区",430412:"南岳区",430421:"衡阳县",430422:"衡南县",430423:"衡山县",430424:"衡东县",430426:"祁东县",430481:"耒阳市",430482:"常宁市"},430500:{430502:"双清区",430503:"大祥区",430511:"北塔区",430521:"邵东县",430522:"新邵县",430523:"邵阳县",430524:"隆回县",430525:"洞口县",430527:"绥宁县",430528:"新宁县",430529:"城步苗族自治县",430581:"武冈市"},430600:{430602:"岳阳楼区",430603:"云溪区",430611:"君山区",430621:"岳阳县",430623:"华容县",430624:"湘阴县",430626:"平江县",430681:"汨罗市",430682:"临湘市"},430700:{430702:"武陵区",430703:"鼎城区",430721:"安乡县",430722:"汉寿县",430723:"澧县",430724:"临澧县",430725:"桃源县",430726:"石门县",430781:"津市市"},430800:{430802:"永定区",430811:"武陵源区",430821:"慈利县",430822:"桑植县"},430900:{430902:"资阳区",430903:"赫山区",430921:"南县",430922:"桃江县",430923:"安化县",430981:"沅江市"},431e3:{431002:"北湖区",431003:"苏仙区",431021:"桂阳县",431022:"宜章县",431023:"永兴县",431024:"嘉禾县",431025:"临武县",431026:"汝城县",431027:"桂东县",431028:"安仁县",431081:"资兴市"},431100:{431102:"零陵区",431103:"冷水滩区",431121:"祁阳县",431122:"东安县",431123:"双牌县",431124:"道县",431125:"江永县",431126:"宁远县",431127:"蓝山县",431128:"新田县",431129:"江华瑶族自治县"},431200:{431202:"鹤城区",431221:"中方县",431222:"沅陵县",431223:"辰溪县",431224:"溆浦县",431225:"会同县",431226:"麻阳苗族自治县",431227:"新晃侗族自治县",431228:"芷江侗族自治县",431229:"靖州苗族侗族自治县",431230:"通道侗族自治县",431281:"洪江市"},431300:{431302:"娄星区",431321:"双峰县",431322:"新化县",431381:"冷水江市",431382:"涟源市"},433100:{433101:"吉首市",433122:"泸溪县",433123:"凤凰县",433124:"花垣县",433125:"保靖县",433126:"古丈县",433127:"永顺县",433130:"龙山县"},44e4:{440100:"广州市",440200:"韶关市",440300:"深圳市",440400:"珠海市",440500:"汕头市",440600:"佛山市",440700:"江门市",440800:"湛江市",440900:"茂名市",441200:"肇庆市",441300:"惠州市",441400:"梅州市",441500:"汕尾市",441600:"河源市",441700:"阳江市",441800:"清远市",441900:"东莞市",442e3:"中山市",442101:"东沙群岛",445100:"潮州市",445200:"揭阳市",445300:"云浮市"},440100:{440103:"荔湾区",440104:"越秀区",440105:"海珠区",440106:"天河区",440111:"白云区",440112:"黄埔区",440113:"番禺区",440114:"花都区",440115:"南沙区",440116:"萝岗区",440118:"增城区",440184:"从化区",440188:"东山区"},440200:{440203:"武江区",440204:"浈江区",440205:"曲江区",440222:"始兴县",440224:"仁化县",440229:"翁源县",440232:"乳源瑶族自治县",440233:"新丰县",440281:"乐昌市",440282:"南雄市"},440300:{440303:"罗湖区",440304:"福田区",440305:"南山区",440306:"宝安区",440307:"龙岗区",440308:"盐田区",440320:"光明新区",440321:"坪山新区",440322:"大鹏新区",440323:"龙华新区"},440400:{440402:"香洲区",440403:"斗门区",440404:"金湾区",440486:"金唐区",440487:"南湾区"},440500:{440507:"龙湖区",440511:"金平区",440512:"濠江区",440513:"潮阳区",440514:"潮南区",440515:"澄海区",440523:"南澳县"},440600:{440604:"禅城区",440605:"南海区",440606:"顺德区",440607:"三水区",440608:"高明区"},440700:{440703:"蓬江区",440704:"江海区",440705:"新会区",440781:"台山市",440783:"开平市",440784:"鹤山市",440785:"恩平市"},440800:{440802:"赤坎区",440803:"霞山区",440804:"坡头区",440811:"麻章区",440823:"遂溪县",440825:"徐闻县",440881:"廉江市",440882:"雷州市",440883:"吴川市"},440900:{440902:"茂南区",440903:"茂港区",440923:"电白区",440981:"高州市",440982:"化州市",440983:"信宜市"},441200:{441202:"端州区",441203:"鼎湖区",441223:"广宁县",441224:"怀集县",441225:"封开县",441226:"德庆县",441283:"高要区",441284:"四会市"},441300:{441302:"惠城区",441303:"惠阳区",441322:"博罗县",441323:"惠东县",441324:"龙门县",441325:"大亚湾区"},441400:{441402:"梅江区",441421:"梅县",441422:"大埔县",441423:"丰顺县",441424:"五华县",441426:"平远县",441427:"蕉岭县",441481:"兴宁市"},441500:{441502:"城区",441521:"海丰县",441523:"陆河县",441581:"陆丰市"},441600:{441602:"源城区",441621:"紫金县",441622:"龙川县",441623:"连平县",441624:"和平县",441625:"东源县"},441700:{441702:"江城区",441721:"阳西县",441723:"阳东区",441781:"阳春市"},441800:{441802:"清城区",441821:"佛冈县",441823:"阳山县",441825:"连山壮族瑶族自治县",441826:"连南瑶族自治县",441827:"清新区",441881:"英德市",441882:"连州市"},442e3:{442002:"中山火炬开发区",442003:"石岐区"},445100:{445102:"湘桥区",445121:"潮安区",445122:"饶平县",445185:"枫溪区"},445200:{445202:"榕城区",445221:"揭东区",445222:"揭西县",445224:"惠来县",445281:"普宁市",445284:"东山区"},445300:{445302:"云城区",445321:"新兴县",445322:"郁南县",445323:"云安区",445381:"罗定市"},45e4:{450100:"南宁市",450200:"柳州市",450300:"桂林市",450400:"梧州市",450500:"北海市",450600:"防城港市",450700:"钦州市",450800:"贵港市",450900:"玉林市",451e3:"百色市",451100:"贺州市",451200:"河池市",451300:"来宾市",451400:"崇左市"},450100:{450102:"兴宁区",450103:"青秀区",450105:"江南区",450107:"西乡塘区",450108:"良庆区",450109:"邕宁区",450110:"武鸣区",450123:"隆安县",450124:"马山县",450125:"上林县",450126:"宾阳县",450127:"横县"},450200:{450202:"城中区",450203:"鱼峰区",450204:"柳南区",450205:"柳北区",450221:"柳江县",450222:"柳城县",450223:"鹿寨县",450224:"融安县",450225:"融水苗族自治县",450226:"三江侗族自治县"},450300:{450302:"秀峰区",450303:"叠彩区",450304:"象山区",450305:"七星区",450311:"雁山区",450321:"阳朔县",450322:"临桂区",450323:"灵川县",450324:"全州县",450325:"兴安县",450326:"永福县",450327:"灌阳县",450328:"龙胜各族自治县",450329:"资源县",450330:"平乐县",450331:"荔浦县",450332:"恭城瑶族自治县"},450400:{450403:"万秀区",450404:"蝶山区",450405:"长洲区",450406:"龙圩区",450421:"苍梧县",450422:"藤县",450423:"蒙山县",450481:"岑溪市"},450500:{450502:"海城区",450503:"银海区",450512:"铁山港区",450521:"合浦县"},450600:{450602:"港口区",450603:"防城区",450621:"上思县",450681:"东兴市"},450700:{450702:"钦南区",450703:"钦北区",450721:"灵山县",450722:"浦北县"},450800:{450802:"港北区",450803:"港南区",450804:"覃塘区",450821:"平南县",450881:"桂平市"},450900:{450902:"玉州区",450903:"福绵区",450921:"容县",450922:"陆川县",450923:"博白县",450924:"兴业县",450981:"北流市"},451e3:{451002:"右江区",451021:"田阳县",451022:"田东县",451023:"平果县",451024:"德保县",451025:"靖西县",451026:"那坡县",451027:"凌云县",451028:"乐业县",451029:"田林县",451030:"西林县",451031:"隆林各族自治县"},451100:{451102:"八步区",451119:"平桂管理区",451121:"昭平县",451122:"钟山县",451123:"富川瑶族自治县"},451200:{451202:"金城江区",451221:"南丹县",451222:"天峨县",451223:"凤山县",451224:"东兰县",451225:"罗城仫佬族自治县",451226:"环江毛南族自治县",451227:"巴马瑶族自治县",451228:"都安瑶族自治县",451229:"大化瑶族自治县",451281:"宜州市"},451300:{451302:"兴宾区",451321:"忻城县",451322:"象州县",451323:"武宣县",451324:"金秀瑶族自治县",451381:"合山市"},451400:{451402:"江州区",451421:"扶绥县",451422:"宁明县",451423:"龙州县",451424:"大新县",451425:"天等县",451481:"凭祥市"},46e4:{460100:"海口市",460200:"三亚市",460300:"三沙市",469001:"五指山市",469002:"琼海市",469003:"儋州市",469005:"文昌市",469006:"万宁市",469007:"东方市",469025:"定安县",469026:"屯昌县",469027:"澄迈县",469028:"临高县",469031:"昌江黎族自治县",469033:"乐东黎族自治县",469034:"陵水黎族自治县",469035:"保亭黎族苗族自治县",469036:"琼中黎族苗族自治县"},460100:{460105:"秀英区",460106:"龙华区",460107:"琼山区",460108:"美兰区"},460200:{460201:"三亚市"},460300:{460321:"西沙群岛",460322:"南沙群岛",460323:"中沙群岛的岛礁及其海域"},5e5:{500100:"重庆市"},500100:{500101:"万州区",500102:"涪陵区",500103:"渝中区",500104:"大渡口区",500105:"江北区",500106:"沙坪坝区",500107:"九龙坡区",500108:"南岸区",500109:"北碚区",500110:"万盛区",500111:"双桥区",500112:"渝北区",500113:"巴南区",500114:"黔江区",500115:"长寿区",500199:"北部新区",500200:"两江新区",500222:"綦江区",500223:"潼南区",500224:"铜梁县",500225:"大足区",500226:"荣昌区",500227:"璧山区",500228:"梁平县",500229:"城口县",500230:"丰都县",500231:"垫江县",500232:"武隆县",500233:"忠县",500234:"开县",500235:"云阳县",500236:"奉节县",500237:"巫山县",500238:"巫溪县",500240:"石柱土家族自治县",500241:"秀山土家族苗族自治县",500242:"酉阳土家族苗族自治县",500243:"彭水苗族土家族自治县",500381:"江津区",500382:"合川区",500383:"永川区",500384:"南川区"},51e4:{510100:"成都市",510300:"自贡市",510400:"攀枝花市",510500:"泸州市",510600:"德阳市",510700:"绵阳市",510800:"广元市",510900:"遂宁市",511e3:"内江市",511100:"乐山市",511300:"南充市",511400:"眉山市",511500:"宜宾市",511600:"广安市",511700:"达州市",511800:"雅安市",511900:"巴中市",512e3:"资阳市",513200:"阿坝藏族羌族自治州",513300:"甘孜藏族自治州",513400:"凉山彝族自治州"},510100:{510104:"锦江区",510105:"青羊区",510106:"金牛区",510107:"武侯区",510108:"成华区",510112:"龙泉驿区",510113:"青白江区",510114:"新都区",510115:"温江区",510116:"天府新区",510117:"高新区",510121:"金堂县",510122:"双流县",510124:"郫县",510129:"大邑县",510131:"蒲江县",510132:"新津县",510181:"都江堰市",510182:"彭州市",510183:"邛崃市",510184:"崇州市"},510300:{510302:"自流井区",510303:"贡井区",510304:"大安区",510311:"沿滩区",510321:"荣县",510322:"富顺县"},510400:{510402:"东区",510403:"西区",510411:"仁和区",510421:"米易县",510422:"盐边县"},510500:{510502:"江阳区",510503:"纳溪区",510504:"龙马潭区",510521:"泸县",510522:"合江县",510524:"叙永县",510525:"古蔺县"},510600:{510603:"旌阳区",510623:"中江县",510626:"罗江县",510681:"广汉市",510682:"什邡市",510683:"绵竹市"},510700:{510703:"涪城区",510704:"游仙区",510722:"三台县",510723:"盐亭县",510724:"安县",510725:"梓潼县",510726:"北川羌族自治县",510727:"平武县",510751:"高新区",510781:"江油市"},510800:{510802:"利州区",510811:"昭化区",510812:"朝天区",510821:"旺苍县",510822:"青川县",510823:"剑阁县",510824:"苍溪县"},510900:{510903:"船山区",510904:"安居区",510921:"蓬溪县",510922:"射洪县",510923:"大英县"},511e3:{511002:"市中区",511011:"东兴区",511024:"威远县",511025:"资中县",511028:"隆昌县"},511100:{511102:"市中区",511111:"沙湾区",511112:"五通桥区",511113:"金口河区",511123:"犍为县",511124:"井研县",511126:"夹江县",511129:"沐川县",511132:"峨边彝族自治县",511133:"马边彝族自治县",511181:"峨眉山市"},511300:{511302:"顺庆区",511303:"高坪区",511304:"嘉陵区",511321:"南部县",511322:"营山县",511323:"蓬安县",511324:"仪陇县",511325:"西充县",511381:"阆中市"},511400:{511402:"东坡区",511421:"仁寿县",511422:"彭山区",511423:"洪雅县",511424:"丹棱县",511425:"青神县"},511500:{511502:"翠屏区",511521:"宜宾县",511522:"南溪区",511523:"江安县",511524:"长宁县",511525:"高县",511526:"珙县",511527:"筠连县",511528:"兴文县",511529:"屏山县"},511600:{511602:"广安区",511603:"前锋区",511621:"岳池县",511622:"武胜县",511623:"邻水县",511681:"华蓥市",511682:"市辖区"},511700:{511702:"通川区",511721:"达川区",511722:"宣汉县",511723:"开江县",511724:"大竹县",511725:"渠县",511781:"万源市"},511800:{511802:"雨城区",511821:"名山区",511822:"荥经县",511823:"汉源县",511824:"石棉县",511825:"天全县",511826:"芦山县",511827:"宝兴县"},511900:{511902:"巴州区",511903:"恩阳区",511921:"通江县",511922:"南江县",511923:"平昌县"},512e3:{512002:"雁江区",512021:"安岳县",512022:"乐至县",512081:"简阳市"},513200:{513221:"汶川县",513222:"理县",513223:"茂县",513224:"松潘县",513225:"九寨沟县",513226:"金川县",513227:"小金县",513228:"黑水县",513229:"马尔康县",513230:"壤塘县",513231:"阿坝县",513232:"若尔盖县",513233:"红原县"},513300:{513321:"康定区",513322:"泸定县",513323:"丹巴县",513324:"九龙县",513325:"雅江县",513326:"道孚县",513327:"炉霍县",513328:"甘孜县",513329:"新龙县",513330:"德格县",513331:"白玉县",513332:"石渠县",513333:"色达县",513334:"理塘县",513335:"巴塘县",513336:"乡城县",513337:"稻城县",513338:"得荣县"},513400:{513401:"西昌市",513422:"木里藏族自治县",513423:"盐源县",513424:"德昌县",513425:"会理县",513426:"会东县",513427:"宁南县",513428:"普格县",513429:"布拖县",513430:"金阳县",513431:"昭觉县",513432:"喜德县",513433:"冕宁县",513434:"越西县",513435:"甘洛县",513436:"美姑县",513437:"雷波县"},52e4:{520100:"贵阳市",520200:"六盘水市",520300:"遵义市",520400:"安顺市",522200:"铜仁市",522300:"黔西南布依族苗族自治州",522400:"毕节市",522600:"黔东南苗族侗族自治州",522700:"黔南布依族苗族自治州"},520100:{520102:"铜仁市",520103:"云岩区",520111:"花溪区",520112:"乌当区",520113:"白云区",520114:"小河区",520121:"开阳县",520122:"息烽县",520123:"修文县",520151:"观山湖区",520181:"清镇市"},520200:{520201:"钟山区",520203:"六枝特区",520221:"水城县",520222:"盘县"},520300:{520302:"红花岗区",520303:"汇川区",520321:"遵义县",520322:"桐梓县",520323:"绥阳县",520324:"正安县",520325:"道真仡佬族苗族自治县",520326:"务川仡佬族苗族自治县",520327:"凤冈县",520328:"湄潭县",520329:"余庆县",520330:"习水县",520381:"赤水市",520382:"仁怀市"},520400:{520402:"西秀区",520421:"平坝区",520422:"普定县",520423:"镇宁布依族苗族自治县",520424:"关岭布依族苗族自治县",520425:"紫云苗族布依族自治县"},522200:{522201:"碧江区",522222:"江口县",522223:"玉屏侗族自治县",522224:"石阡县",522225:"思南县",522226:"印江土家族苗族自治县",522227:"德江县",522228:"沿河土家族自治县",522229:"松桃苗族自治县",522230:"万山区"},522300:{522301:"兴义市",522322:"兴仁县",522323:"普安县",522324:"晴隆县",522325:"贞丰县",522326:"望谟县",522327:"册亨县",522328:"安龙县"},522400:{520502:"七星关区",520521:"大方县",520522:"黔西县",520523:"金沙县",520524:"织金县",520525:"纳雍县",520526:"威宁彝族回族苗族自治县",520527:"赫章县"},522600:{522601:"凯里市",522622:"黄平县",522623:"施秉县",522624:"三穗县",522625:"镇远县",522626:"岑巩县",522627:"天柱县",522628:"锦屏县",522629:"剑河县",522630:"台江县",522631:"黎平县",522632:"榕江县",522633:"从江县",522634:"雷山县",522635:"麻江县",522636:"丹寨县"},522700:{522701:"都匀市",522702:"福泉市",522722:"荔波县",522723:"贵定县",522725:"瓮安县",522726:"独山县",522727:"平塘县",522728:"罗甸县",522729:"长顺县",522730:"龙里县",522731:"惠水县",522732:"三都水族自治县"},53e4:{530100:"昆明市",530300:"曲靖市",530400:"玉溪市",530500:"保山市",530600:"昭通市",530700:"丽江市",530800:"普洱市",530900:"临沧市",532300:"楚雄彝族自治州",532500:"红河哈尼族彝族自治州",532600:"文山壮族苗族自治州",532800:"西双版纳傣族自治州",532900:"大理白族自治州",533100:"德宏傣族景颇族自治州",533300:"怒江傈僳族自治州",533400:"迪庆藏族自治州"},530100:{530102:"五华区",530103:"盘龙区",530111:"官渡区",530112:"西山区",530113:"东川区",530114:"呈贡区",530122:"晋宁县",530124:"富民县",530125:"宜良县",530126:"石林彝族自治县",530127:"嵩明县",530128:"禄劝彝族苗族自治县",530129:"寻甸回族彝族自治县",530181:"安宁市"},530300:{530302:"麒麟区",530321:"马龙县",530322:"陆良县",530323:"师宗县",530324:"罗平县",530325:"富源县",530326:"会泽县",530328:"沾益县",530381:"宣威市"},530400:{530402:"红塔区",530421:"江川县",530422:"澄江县",530423:"通海县",530424:"华宁县",530425:"易门县",530426:"峨山彝族自治县",530427:"新平彝族傣族自治县",530428:"元江哈尼族彝族傣族自治县"},530500:{530502:"隆阳区",530521:"施甸县",530522:"腾冲县",530523:"龙陵县",530524:"昌宁县"},530600:{530602:"昭阳区",530621:"鲁甸县",530622:"巧家县",530623:"盐津县",530624:"大关县",530625:"永善县",530626:"绥江县",530627:"镇雄县",530628:"彝良县",530629:"威信县",530630:"水富县"},530700:{530702:"古城区",530721:"玉龙纳西族自治县",530722:"永胜县",530723:"华坪县",530724:"宁蒗彝族自治县"},530800:{530802:"思茅区",530821:"宁洱哈尼族彝族自治县",530822:"墨江哈尼族自治县",530823:"景东彝族自治县",530824:"景谷傣族彝族自治县",530825:"镇沅彝族哈尼族拉祜族自治县",530826:"江城哈尼族彝族自治县",530827:"孟连傣族拉祜族佤族自治县",530828:"澜沧拉祜族自治县",530829:"西盟佤族自治县"},530900:{530902:"临翔区",530921:"凤庆县",530922:"云县",530923:"永德县",530924:"镇康县",530925:"双江拉祜族佤族布朗族傣族自治县",530926:"耿马傣族佤族自治县",530927:"沧源佤族自治县"},532300:{532301:"楚雄市",532322:"双柏县",532323:"牟定县",532324:"南华县",532325:"姚安县",532326:"大姚县",532327:"永仁县",532328:"元谋县",532329:"武定县",532331:"禄丰县"},532500:{532501:"个旧市",532502:"开远市",532522:"蒙自市",532523:"屏边苗族自治县",532524:"建水县",532525:"石屏县",532526:"弥勒市",532527:"泸西县",532528:"元阳县",532529:"红河县",532530:"金平苗族瑶族傣族自治县",532531:"绿春县",532532:"河口瑶族自治县"},532600:{532621:"文山市",532622:"砚山县",532623:"西畴县",532624:"麻栗坡县",532625:"马关县",532626:"丘北县",532627:"广南县",532628:"富宁县"},532800:{532801:"景洪市",532822:"勐海县",532823:"勐腊县"},532900:{532901:"大理市",532922:"漾濞彝族自治县",532923:"祥云县",532924:"宾川县",532925:"弥渡县",532926:"南涧彝族自治县",532927:"巍山彝族回族自治县",532928:"永平县",532929:"云龙县",532930:"洱源县",532931:"剑川县",532932:"鹤庆县"},533100:{533102:"瑞丽市",533103:"芒市",533122:"梁河县",533123:"盈江县",533124:"陇川县"},533300:{533321:"泸水县",533323:"福贡县",533324:"贡山独龙族怒族自治县",533325:"兰坪白族普米族自治县"},533400:{533421:"香格里拉市",533422:"德钦县",533423:"维西傈僳族自治县"},54e4:{540100:"拉萨市",542300:"日喀则市",542100:"昌都市",542200:"山南地区",542400:"那曲地区",542500:"阿里地区",542600:"林芝市"},540100:{540102:"城关区",540121:"林周县",540122:"当雄县",540123:"尼木县",540124:"曲水县",540125:"堆龙德庆县",540126:"达孜县",540127:"墨竹工卡县"},542300:{542301:"桑珠孜区",542322:"南木林县",542323:"江孜县",542324:"定日县",542325:"萨迦县",542326:"拉孜县",542327:"昂仁县",542328:"谢通门县",542329:"白朗县",542330:"仁布县",542331:"康马县",542332:"定结县",542333:"仲巴县",542334:"亚东县",542335:"吉隆县",542336:"聂拉木县",542337:"萨嘎县",542338:"岗巴县"},542100:{542121:"卡若区",542122:"江达县",542123:"贡觉县",542124:"类乌齐县",542125:"丁青县",542126:"察雅县",542127:"八宿县",542128:"左贡县",542129:"芒康县",542132:"洛隆县",542133:"边坝县"},542200:{542221:"乃东县",542222:"扎囊县",542223:"贡嘎县",542224:"桑日县",542225:"琼结县",542226:"曲松县",542227:"措美县",542228:"洛扎县",542229:"加查县",542231:"隆子县",542232:"错那县",542233:"浪卡子县"},542400:{542421:"那曲县",542422:"嘉黎县",542423:"比如县",542424:"聂荣县",542425:"安多县",542426:"申扎县",542427:"索县",542428:"班戈县",542429:"巴青县",542430:"尼玛县",542432:"双湖县"},542500:{542521:"普兰县",542522:"札达县",542523:"噶尔县",542524:"日土县",542525:"革吉县",542526:"改则县",542527:"措勤县"},542600:{542621:"巴宜区",542622:"工布江达县",542623:"米林县",542624:"墨脱县",542625:"波密县",542626:"察隅县",542627:"朗县"},61e4:{610100:"西安市",610200:"铜川市",610300:"宝鸡市",610400:"咸阳市",610500:"渭南市",610600:"延安市",610700:"汉中市",610800:"榆林市",610900:"安康市",611e3:"商洛市"},610100:{610102:"新城区",610103:"碑林区",610104:"莲湖区",610111:"灞桥区",610112:"未央区",610113:"雁塔区",610114:"阎良区",610115:"临潼区",610116:"长安区",610117:"高陵区",610122:"蓝田县",610124:"周至县",610125:"户县",610126:"高陵区",610199:"曲江新区"},610200:{610202:"王益区",610203:"印台区",610204:"耀州区",610222:"宜君县"},610300:{610302:"渭滨区",610303:"金台区",610304:"陈仓区",610322:"凤翔县",610323:"岐山县",610324:"扶风县",610326:"眉县",610327:"陇县",610328:"千阳县",610329:"麟游县",610330:"凤县",610331:"太白县"},610400:{610402:"秦都区",610403:"杨陵区",610404:"渭城区",610422:"三原县",610423:"泾阳县",610424:"乾县",610425:"礼泉县",610426:"永寿县",610427:"彬县",610428:"长武县",610429:"旬邑县",610430:"淳化县",610431:"武功县",610481:"兴平市"},610500:{610502:"临渭区",610521:"华县",610522:"潼关县",610523:"大荔县",610524:"合阳县",610525:"澄城县",610526:"蒲城县",610527:"白水县",610528:"富平县",610581:"韩城市",610582:"华阴市"},610600:{610602:"宝塔区",610621:"延长县",610622:"延川县",610623:"子长县",610624:"安塞县",610625:"志丹县",610626:"吴起县",610627:"甘泉县",610628:"富县",610629:"洛川县",610630:"宜川县",610631:"黄龙县",610632:"黄陵县"},610700:{610702:"汉台区",610721:"南郑县",610722:"城固县",610723:"洋县",610724:"西乡县",610725:"勉县",610726:"宁强县",610727:"略阳县",610728:"镇巴县",610729:"留坝县",610730:"佛坪县"},610800:{610802:"榆阳区",610821:"神木县",610822:"府谷县",610823:"横山县",610824:"靖边县",610825:"定边县",610826:"绥德县",610827:"米脂县",610828:"佳县",610829:"吴堡县",610830:"清涧县",610831:"子洲县"},610900:{610902:"汉滨区",610921:"汉阴县",610922:"石泉县",610923:"宁陕县",610924:"紫阳县",610925:"岚皋县",610926:"平利县",610927:"镇坪县",610928:"旬阳县",610929:"白河县"},611e3:{611002:"商州区",611021:"洛南县",611022:"丹凤县",611023:"商南县",611024:"山阳县",611025:"镇安县",611026:"柞水县"},62e4:{620100:"兰州市",620200:"嘉峪关市",620300:"金昌市",620400:"白银市",620500:"天水市",620600:"武威市",620700:"张掖市",620800:"平凉市",620900:"酒泉市",621e3:"庆阳市",621100:"定西市",621200:"陇南市",622900:"临夏回族自治州",623e3:"甘南藏族自治州"},620100:{1000665:"兰州新区",620102:"城关区",620103:"七里河区",620104:"西固区",620105:"安宁区",620111:"红古区",620121:"永登县",620122:"皋兰县",620123:"榆中县"},620300:{620302:"金川区",620321:"永昌县"},620400:{620402:"白银区",620403:"平川区",620421:"靖远县",620422:"会宁县",620423:"景泰县"},620500:{620502:"秦州区",620503:"麦积区",620521:"清水县",620522:"秦安县",620523:"甘谷县",620524:"武山县",620525:"张家川回族自治县"},620600:{620602:"凉州区",620621:"民勤县",620622:"古浪县",620623:"天祝藏族自治县"},620700:{620702:"甘州区",620721:"肃南裕固族自治县",620722:"民乐县",620723:"临泽县",620724:"高台县",620725:"山丹县"},620800:{620802:"崆峒区",620821:"泾川县",620822:"灵台县",620823:"崇信县",620824:"华亭县",620825:"庄浪县",620826:"静宁县"},620900:{620902:"肃州区",620921:"金塔县",620922:"瓜州县",620923:"肃北蒙古族自治县",620924:"阿克塞哈萨克族自治县",620981:"玉门市",620982:"敦煌市"},621e3:{621002:"西峰区",621021:"庆城县",621022:"环县",621023:"华池县",621024:"合水县",621025:"正宁县",621026:"宁县",621027:"镇原县"},621100:{621102:"安定区",621121:"通渭县",621122:"陇西县",621123:"渭源县",621124:"临洮县",621125:"漳县",621126:"岷县"},621200:{621202:"武都区",621221:"成县",621222:"文县",621223:"宕昌县",621224:"康县",621225:"西和县",621226:"礼县",621227:"徽县",621228:"两当县"},622900:{622901:"临夏市",622921:"临夏县",622922:"康乐县",622923:"永靖县",622924:"广河县",622925:"和政县",622926:"东乡族自治县",622927:"积石山保安族东乡族撒拉族自治县"},623e3:{623001:"合作市",623021:"临潭县",623022:"卓尼县",623023:"舟曲县",623024:"迭部县",623025:"玛曲县",623026:"碌曲县",623027:"夏河县"},63e4:{630100:"西宁市",632100:"海东市",632200:"海北藏族自治州",632300:"黄南藏族自治州",632500:"海南藏族自治州",632600:"果洛藏族自治州",632700:"玉树藏族自治州",632800:"海西蒙古族藏族自治州"},630100:{630102:"城东区",630103:"城中区",630104:"城西区",630105:"城北区",630121:"大通回族土族自治县",630122:"湟中县",630123:"湟源县"},632100:{632121:"平安区",632122:"民和回族土族自治县",632123:"乐都区",632126:"互助土族自治县",632127:"化隆回族自治县",632128:"循化撒拉族自治县"},632200:{632221:"门源回族自治县",632222:"祁连县",632223:"海晏县",632224:"刚察县"},632300:{632321:"同仁县",632322:"尖扎县",632323:"泽库县",632324:"河南蒙古族自治县"},632500:{632521:"共和县",632522:"同德县",632523:"贵德县",632524:"兴海县",632525:"贵南县"},632600:{632621:"玛沁县",632622:"班玛县",632623:"甘德县",632624:"达日县",632625:"久治县",632626:"玛多县"},632700:{632721:"玉树市",632722:"杂多县",632723:"称多县",632724:"治多县",632725:"囊谦县",632726:"曲麻莱县"},632800:{632801:"格尔木市",632802:"德令哈市",632821:"乌兰县",632822:"都兰县",632823:"天峻县"},64e4:{640100:"银川市",640200:"石嘴山市",640300:"吴忠市",640400:"固原市",640500:"中卫市"},640100:{640104:"兴庆区",640105:"西夏区",640106:"金凤区",640121:"永宁县",640122:"贺兰县",640181:"灵武市"},640200:{640202:"大武口区",640205:"惠农区",640221:"平罗县"},640300:{640302:"利通区",640303:"红寺堡区",640323:"盐池县",640324:"同心县",640381:"青铜峡市"},640400:{640402:"原州区",640422:"西吉县",640423:"隆德县",640424:"泾源县",640425:"彭阳县"},640500:{640502:"沙坡头区",640521:"中宁县",640522:"海原县"},65e4:{650100:"乌鲁木齐市",650200:"克拉玛依市",652100:"吐鲁番市",652200:"哈密地区",652300:"昌吉回族自治州",652700:"博尔塔拉蒙古自治州",652800:"巴音郭楞蒙古自治州",652900:"阿克苏地区",653e3:"克孜勒苏柯尔克孜自治州",653100:"喀什地区",653200:"和田地区",654e3:"伊犁哈萨克自治州",654200:"塔城地区",654300:"阿勒泰地区",659001:"石河子市",659002:"阿拉尔市",659003:"图木舒克市",659004:"五家渠市",659005:"北屯市",659006:"铁门关市",659008:"可克达拉市"},650100:{650102:"天山区",650103:"沙依巴克区",650104:"新市区",650105:"水磨沟区",650106:"头屯河区",650107:"达坂城区",650109:"米东区",650121:"乌鲁木齐县"},650200:{650202:"独山子区",650203:"克拉玛依区",650204:"白碱滩区",650205:"乌尔禾区"},652100:{652101:"高昌区",652122:"鄯善县",652123:"托克逊县"},652200:{652201:"哈密市",652222:"巴里坤哈萨克自治县",652223:"伊吾县"},652300:{652301:"昌吉市",652302:"阜康市",652323:"呼图壁县",652324:"玛纳斯县",652325:"奇台县",652327:"吉木萨尔县",652328:"木垒哈萨克自治县"},652700:{652701:"博乐市",652702:"阿拉山口市",652722:"精河县",652723:"温泉县"},652800:{652801:"库尔勒市",652822:"轮台县",652823:"尉犁县",652824:"若羌县",652825:"且末县",652826:"焉耆回族自治县",652827:"和静县",652828:"和硕县",652829:"博湖县"},652900:{652901:"阿克苏市",652922:"温宿县",652923:"库车县",652924:"沙雅县",652925:"新和县",652926:"拜城县",652927:"乌什县",652928:"阿瓦提县",652929:"柯坪县"},653e3:{653001:"阿图什市",653022:"阿克陶县",653023:"阿合奇县",653024:"乌恰县"},653100:{653101:"喀什市",653121:"疏附县",653122:"疏勒县",653123:"英吉沙县",653124:"泽普县",653125:"莎车县",653126:"叶城县",653127:"麦盖提县",653128:"岳普湖县",653129:"伽师县",653130:"巴楚县",653131:"塔什库尔干塔吉克自治县"},653200:{653201:"和田市",653221:"和田县",653222:"墨玉县",653223:"皮山县",653224:"洛浦县",653225:"策勒县",653226:"于田县",653227:"民丰县"},654e3:{654002:"伊宁市",654003:"奎屯市",654004:"霍尔果斯市",654021:"伊宁县",654022:"察布查尔锡伯自治县",654023:"霍城县",654024:"巩留县",654025:"新源县",654026:"昭苏县",654027:"特克斯县",654028:"尼勒克县"},654200:{654201:"塔城市",654202:"乌苏市",654221:"额敏县",654223:"沙湾县",654224:"托里县",654225:"裕民县",654226:"和布克赛尔蒙古自治县"},654300:{654301:"阿勒泰市",654321:"布尔津县",654322:"富蕴县",654323:"福海县",654324:"哈巴河县",654325:"青河县",654326:"吉木乃县"},81e4:{810100:"九龙",810200:"香港岛",810300:"新界"},810100:{810101:"观塘区",810102:"黄大仙区",810103:"九龙城区",810104:"深水埗区",810105:"油尖旺区"},810200:{810201:"东区",810202:"南区",810203:"湾仔区",810204:"中西区"},810300:{810301:"北区",810302:"大埔区",810303:"葵青区",810304:"离岛区",810305:"荃湾区",810306:"沙田区",810307:"屯门区",810308:"西贡区",810309:"元朗区"},82e4:{820100:"离岛",820200:"澳门半岛"},820100:{820101:"嘉模堂区",820102:"路氹填海区",820103:"圣方济各堂区"},820200:{820201:"大堂区",820202:"风顺堂区",820203:"花地玛堂区",820204:"花王堂区",820205:"望德堂区"},83e4:{830100:"台北市",830200:"新北市",830300:"桃园市",830400:"台中市",830500:"台南市",830600:"高雄市", +830700:"基隆市",830800:"新竹市",830900:"嘉义市"},830100:{830101:"中正区",830102:"大同区",830103:"中山区",830104:"万华区",830105:"信义区",830106:"松山区",830107:"大安区",830108:"南港区",830109:"北投区",830110:"内湖区",830111:"士林区",830112:"文山区"},830200:{830201:"板桥区",830202:"土城区",830203:"新庄区",830204:"新店区",830205:"深坑区",830206:"石碇区",830207:"坪林区",830208:"乌来区",830209:"五股区",830210:"八里区",830211:"林口区",830212:"淡水区",830213:"中和区",830214:"永和区",830215:"三重区",830216:"芦洲区",830217:"泰山区",830218:"树林区",830219:"莺歌区",830220:"三峡区",830221:"汐止区",830222:"金山区",830223:"万里区",830224:"三芝区",830225:"石门区",830226:"瑞芳区",830227:"贡寮区",830228:"双溪区",830229:"平溪区"},830300:{830301:"桃园区",830302:"中坜区",830303:"平镇区",830304:"八德区",830305:"杨梅区",830306:"芦竹区",830307:"大溪区",830308:"龙潭区",830309:"龟山区",830310:"大园区",830311:"观音区",830312:"新屋区",830313:"复兴区"},830400:{830401:"中区",830402:"东区",830403:"西区",830404:"南区",830405:"北区",830406:"西屯区",830407:"南屯区",830408:"北屯区",830409:"丰原区",830410:"大里区",830411:"太平区",830412:"东势区",830413:"大甲区",830414:"清水区",830415:"沙鹿区",830416:"梧栖区",830417:"后里区",830418:"神冈区",830419:"潭子区",830420:"大雅区",830421:"新小区",830422:"石冈区",830423:"外埔区",830424:"大安区",830425:"乌日区",830426:"大肚区",830427:"龙井区",830428:"雾峰区",830429:"和平区"},830500:{830501:"中西区",830502:"东区",830503:"南区",830504:"北区",830505:"安平区",830506:"安南区",830507:"永康区",830508:"归仁区",830509:"新化区",830510:"左镇区",830511:"玉井区",830512:"楠西区",830513:"南化区",830514:"仁德区",830515:"关庙区",830516:"龙崎区",830517:"官田区",830518:"麻豆区",830519:"佳里区",830520:"西港区",830521:"七股区",830522:"将军区",830523:"学甲区",830524:"北门区",830525:"新营区",830526:"后壁区",830527:"白河区",830528:"东山区",830529:"六甲区",830530:"下营区",830531:"柳营区",830532:"盐水区",830533:"善化区",830534:"大内区",830535:"山上区",830536:"新市区",830537:"安定区"},830600:{830601:"楠梓区",830602:"左营区",830603:"鼓山区",830604:"三民区",830605:"盐埕区",830606:"前金区",830607:"新兴区",830608:"苓雅区",830609:"前镇区",830610:"旗津区",830611:"小港区",830612:"凤山区",830613:"大寮区",830614:"鸟松区",830615:"林园区",830616:"仁武区",830617:"大树区",830618:"大社区",830619:"冈山区",830620:"路竹区",830621:"桥头区",830622:"梓官区",830623:"弥陀区",830624:"永安区",830625:"燕巢区",830626:"阿莲区",830627:"茄萣区",830628:"湖内区",830629:"旗山区",830630:"美浓区",830631:"内门区",830632:"杉林区",830633:"甲仙区",830634:"六龟区",830635:"茂林区",830636:"桃源区",830637:"那玛夏区"},830700:{830701:"中正区",830702:"七堵区",830703:"暖暖区",830704:"仁爱区",830705:"中山区",830706:"安乐区",830707:"信义区"},830800:{830801:"东区",830802:"北区",830803:"香山区"},830900:{830901:"东区",830902:"西区"}};"undefined"!=typeof window&&(window.ChineseDistricts=ChineseDistricts),define("plugins/lay-module/cityPicker/city-picker-data",function(){}),define("plugins/require-css/css.min!plugins/lay-module/cityPicker/city-picker",[],function(){}),layui.define(["jquery"],function(exports){var $=layui.$;if(void 0===ChineseDistricts)throw new Error('The file "city-picker.data.js" must be included first!');var EVENT_CHANGE="change.citypicker",CityPicker=function(element,options){this.PROVINCE="provinceId",this.CITY="cityId",this.DISTRICT="districtId",this.$element=$(element),this.$dropdown=null,this.options=$.extend({},CityPicker.DEFAULTS,$.isPlainObject(options)&&options),this.active=!1,this.dems=[],this.needBlur=!1,this.init()};CityPicker.prototype={constructor:CityPicker,init:function(){this.defineDems(),this.render(),this.bind(),this.active=!0;var _this=this;$(".icon_ca").on("click",function(){$("#"+this.PROVINCE).val(),$("#"+this.CITY).val(),$("#"+this.DISTRICT).val(),_this.reset()})},render:function(){var p=this.getPosition(),placeholder=this.$element.attr("placeholder")||this.options.placeholder,textspan=''+(placeholder?''+placeholder+"":"")+'
                ',dropdown='
                省份'+(this.includeDem(this.CITY)?'城市':"")+(this.includeDem(this.DISTRICT)?'区县':"")+'
                '+(this.includeDem(this.CITY)?'
                ':"")+(this.includeDem(this.DISTRICT)?'
                ':"")+"
                ";this.$element.addClass("city-picker-input"),this.$textspan=$(textspan).insertBefore(this.$element),this.$dropdown=$(dropdown).insertAfter(this.$textspan);var $select=this.$dropdown.find(".city-select");$.each(this.dems,$.proxy(function(i,type){this["$"+type]=$select.filter("."+type)},this)),this.refresh()},refresh:function(force){this.$dropdown.find(".city-select").data("item",null);var val=this.$element.val()||"";val=val.split("/"),$.each(this.dems,$.proxy(function(i,type){val[i]&&i.99&&(w=1),w=100*w+"%"),{top:p.top||0,left:p.left||0,height:h,width:w}},getSize:function($dom){var $wrap,$clone,sizes;return $dom.is(":visible")?sizes={width:$dom.outerWidth(),height:$dom.outerHeight()}:($wrap=$("
                ").appendTo($("body")),$wrap.css({position:"absolute !important",visibility:"hidden !important",display:"block !important"}),$clone=$dom.clone().appendTo($wrap),sizes={width:$clone.outerWidth(),height:$clone.outerHeight()},$wrap.remove()),sizes},getWidthStyle:function(w,dropdown){return w="100%",this.options.responsive&&!$.isNumeric(w)?"width:"+w+";":"width:"+(dropdown?Math.max(320,w):w)+"px;"},bind:function(){var $this=this;$(document).on("click",this._mouteclick=function(e){var $dropdown,$span,$input,$target=$(e.target);$target.is(".city-picker-span")?$span=$target:$target.is(".city-picker-span *")&&($span=$target.parents(".city-picker-span")),$target.is(".city-picker-input")&&($input=$target),$target.is(".city-picker-dropdown")?$dropdown=$target:$target.is(".city-picker-dropdown *")&&($dropdown=$target.parents(".city-picker-dropdown")),(!$input&&!$span&&!$dropdown||$span&&$span.get(0)!==$this.$textspan.get(0)||$input&&$input.get(0)!==$this.$element.get(0)||$dropdown&&$dropdown.get(0)!==$this.$dropdown.get(0))&&$this.close(!0)}),this.$element.on("change",this._changeElement=$.proxy(function(){this.close(!0),this.refresh(!0)},this)).on("focus",this._focusElement=$.proxy(function(){this.needBlur=!0,this.open()},this)).on("blur",this._blurElement=$.proxy(function(){this.needBlur&&(this.needBlur=!1,this.close(!0))},this)),this.$textspan.on("click",function(e){var type,$target=$(e.target);$this.needBlur=!1,$target.is(".select-item")?(type=$target.data("count"),$this.open(type)):$this.$dropdown.is(":visible")?$this.close():$this.open()}).on("mousedown",function(){$this.needBlur=!1}),this.$dropdown.on("click",".city-select a",function(){var $select=$(this).parents(".city-select"),$active=$select.find("a.active"),last=0===$select.next().length;$active.removeClass("active"),$(this).addClass("active"),$active.data("code")!==$(this).data("code")&&($select.data("item",{address:$(this).attr("title"),code:$(this).data("code")}),$(this).trigger(EVENT_CHANGE),$this.feedText(),$this.feedVal(),last&&$this.close())}).on("click",".city-select-tab a",function(){if(!$(this).hasClass("active")){var type=$(this).data("count");$this.tab(type)}}).on("mousedown",function(){$this.needBlur=!1}),this["$"+this.PROVINCE]&&this["$"+this.PROVINCE].on(EVENT_CHANGE,this._changeProvince=$.proxy(function(){this.output(this.CITY),this.output(this.DISTRICT),this.tab(this.CITY)},this)),this["$"+this.CITY]&&this["$"+this.CITY].on(EVENT_CHANGE,this._changeCity=$.proxy(function(){this.output(this.DISTRICT),this.tab(this.DISTRICT)},this))},open:function(type){type=type||this.PROVINCE,this.$dropdown.show(),this.$textspan.addClass("open").addClass("focus"),this.tab(type)},close:function(blur){this.$dropdown.hide(),this.$textspan.removeClass("open"),blur&&this.$textspan.removeClass("focus")},unbind:function(){$(document).off("click",this._mouteclick),this.$element.off("change",this._changeElement),this.$element.off("focus",this._focusElement),this.$element.off("blur",this._blurElement),this.$textspan.off("click"),this.$textspan.off("mousedown"),this.$dropdown.off("click"),this.$dropdown.off("mousedown"),this["$"+this.PROVINCE]&&this["$"+this.PROVINCE].off(EVENT_CHANGE,this._changeProvince),this.$city&&this.$city.off(EVENT_CHANGE,this._changeCity)},getText:function(){var text="";return this.$dropdown.find(".city-select").each(function(){var item=$(this).data("item"),type=$(this).data("count");item&&(text+=($(this).hasClass("province")?"":"/")+''+item.address+'')}),text},getPlaceHolder:function(){return this.$element.attr("placeholder")||this.options.placeholder},feedText:function(){this.getText()?(this.$textspan.find(">.placeholder").hide(),this.$textspan.find(">.title").children("input").val(""),this.$textspan.find(">.title").html(this.getText()).show()):(this.$textspan.find(">.placeholder").text(this.getPlaceHolder()).show(),this.$textspan.find(">.title").html("").hide())},getVal:function(){var text="";return this.$dropdown.find(".city-select").each(function(){var item=$(this).data("item");item&&(text+=($(this).hasClass("province")?"":"/")+item.address)}),text},feedVal:function(){this.$element.val(this.getVal())},output:function(type){var item,districts,code,value,options=this.options,PROVINCE=this.PROVINCE,CITY=this.CITY,DISTRICT=this.DISTRICT,$select=this["$"+type],data=type===PROVINCE?{}:[],matched=null;$select&&$select.length&&(item=$select.data("item"),value=(item?item.address:null)||options[type],code=type===PROVINCE?86:type===CITY?this["$"+PROVINCE]&&this["$"+PROVINCE].find(".active").data("code"):type===DISTRICT?this["$"+CITY]&&this["$"+CITY].find(".active").data("code"):code,districts=$.isNumeric(code)?ChineseDistricts[code]:null,$.isPlainObject(districts)&&$.each(districts,function(code,address){var provs;if(type===PROVINCE){provs=[];for(var i=0;i'),list.push("
                "+i+"
                "),$.each(n,function(j,m){list.push(''+(simple?$this.simplize(m.address,PROVINCE):m.address)+"")}),list.push("
                ")}),list.join("")},getList:function(data,type){var list=[],$this=this,simple=this.options.simple;return list.push('
                '),$.each(data,function(i,n){list.push(''+(simple?$this.simplize(n.address,type):n.address)+"")}),list.push("
                "),list.join("")},simplize:function(address,type){return address=address||"",type===this.PROVINCE?address.replace(/[省,市,自治区,壮族,回族,维吾尔]/g,""):type===this.CITY?address.replace(/[市,地区,回族,蒙古,苗族,白族,傣族,景颇族,藏族,彝族,壮族,傈僳族,布依族,侗族]/g,"").replace("哈萨克","").replace("自治州","").replace(/自治县/,""):type===this.DISTRICT?address.length>2?address.replace(/[市,区,县,旗]/g,""):address:void 0},tab:function(type){var $selects=this.$dropdown.find(".city-select"),$tabs=this.$dropdown.find(".city-select-tab > a"),$select=this["$"+type],$tab=this.$dropdown.find('.city-select-tab > a[data-count="'+type+'"]');$select&&($selects.hide(),$select.show(),$tabs.removeClass("active"),$tab.addClass("active"))},reset:function(){this.$element.val(null).trigger("change")},setValue:function(address){this.$element.val(address).trigger("change")},destroy:function(){this.unbind(),this.$element.removeData("citypicker").removeClass("city-picker-input"),this.$textspan.remove(),this.$dropdown.remove()}},CityPicker.DEFAULTS={simple:!1,responsive:!1,placeholder:"请选择省/市/区",level:"district",provincename:"",cityname:"",districtname:"",province:"河南省",city:"",district:""},CityPicker.setDefaults=function(options){$.extend(CityPicker.DEFAULTS,options)},CityPicker.other=$.fn.citypicker,$.fn.citypicker=function(option){var args=[].slice.call(arguments,1);return this.each(function(){var options,fn,$this=$(this),data=$this.data("citypicker");if(!data){if(/destroy/.test(option))return;options=$.extend({},$this.data(),$.isPlainObject(option)&&option),$this.data("citypicker",data=new CityPicker(this,options))}"string"==typeof option&&$.isFunction(fn=data[option])&&fn.apply(data,args)})},$.fn.citypicker.Constructor=CityPicker,$.fn.citypicker.setDefaults=CityPicker.setDefaults,$.fn.citypicker.noConflict=function(){return $.fn.citypicker=CityPicker.other,this},exports("cityPicker",CityPicker)}),define("cityPicker",["plugins/lay-module/cityPicker/city-picker-data","css!plugins/lay-module/cityPicker/city-picker.css"],function(){}),layui.define([],function(exports){function toDate(input){return input instanceof Date?input:isNaN(input)?/^\d+$/.test(input)?new Date(toInt(input)):(input=(input||"").trim().replace(/\.\d+/,"").replace(/-/,"/").replace(/-/,"/").replace(/(\d)T(\d)/,"$1 $2").replace(/Z/," UTC").replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"),new Date(input)):new Date(toInt(input))}function toInt(f){return parseInt(f)}function formatDiff(diff,locale,defaultLocale){locale=locales[locale]?locale:locales[defaultLocale]?defaultLocale:"zh_CN";for(var i=0,agoin=diff<0?1:0,total_sec=diff=Math.abs(diff);diff>=SEC_ARRAY[i]&&i(0===i?9:1)&&(i+=1),locales[locale](diff,i,total_sec)[agoin].replace("%s",diff)}function diffSec(date,nowDate){return((nowDate=nowDate?toDate(nowDate):new Date)-toDate(date))/1e3}function nextInterval(diff){for(var rst=1,i=0,d=Math.abs(diff);diff>=SEC_ARRAY[i]&&i1&&(unit+="s"),[number+" "+unit+" ago","in "+number+" "+unit]},zh_CN:function(number,index){if(0===index)return["刚刚","片刻后"];var unit=indexMapZh[parseInt(index/2)];return[number+unit+"前",number+unit+"后"]}},SEC_ARRAY=[60,60,24,7,365/7/12,12],SEC_ARRAY_LEN=6,ATTR_DATETIME="datetime",ATTR_DATA_TID="data-tid",timers={};Timeago.prototype.doRender=function(node,date,locale){var tid,diff=diffSec(date,this.nowDate),self=this;node.innerHTML=formatDiff(diff,locale,this.defaultLocale),timers[tid=setTimeout(function(){self.doRender(node,date,locale),delete timers[tid]},Math.min(1e3*nextInterval(diff),2147483647))]=0,setTidAttr(node,tid)},Timeago.prototype.format=function(date,locale){return formatDiff(diffSec(date,this.nowDate),locale,this.defaultLocale)},Timeago.prototype.render=function(nodes,locale){void 0===nodes.length&&(nodes=[nodes]);for(var i=0,len=nodes.length;i=self.selectArray.length)){var _selectData,_valueIndex,_queryName,_selectName,_selectValue,_select=self.selectArray[index],_dataUrl=_select.data("url"),_jsonSpace=void 0===_select.data("jsonSpace")?self.settings.jsonSpace:_select.data("jsonSpace"),_query={};if(cxSelect.clear.call(self,index),"string"==typeof _dataUrl&&_dataUrl.length){if(index>0)for(var i=0,j=1;i'+String(_firstTitle)+"";if("string"==typeof _jsonName&&_jsonName.length){"string"==typeof _jsonValue&&_jsonValue.length||(_jsonValue=_jsonName);for(var i=0,l=data.length;i'+String(data[i][_jsonName])+""}else for(var i=0,l=data.length;i'+String(data[i])+"";_select.html(_html).prop("disabled",!1).css({display:"",visibility:""}),"string"==typeof _select.attr("data-value")&&(_select.val(String(_select.attr("data-value"))).removeAttr("data-value"),_select[0].selectedIndex<0&&(_select[0].options[0].selected=!0)),(_required||_select[0].selectedIndex>0)&&_select.trigger("change"),form.render("select")}},cxSelect.selectChange=function(name){var self=this;if("string"==typeof name&&name.length){var index;name=name.replace(/\s+/g,","),name=","+name+",";for(var i=0,l=self.selectArray.length;i-1){index=i;break}"number"==typeof index&&index>-1&&(index+=1,cxSelect.getOptionData.call(self,index))}},$.cxSelect=function(){return cxSelect.apply(this,arguments)},$.cxSelect.defaults={selects:[],url:null,data:null,emptyStyle:null,required:!1,firstTitle:"请选择",firstValue:"",jsonSpace:"",jsonName:"n",jsonValue:"",jsonSub:"s"},$.fn.cxSelect=function(settings,callback){return this.each(function(i){$.cxSelect(this,settings,callback)}),this}}),exports("cxSelect",{})}),layui.define(["jquery","dropdown"],function(exports){"use strict";var $=layui.jquery,laytpl=layui.laytpl,dropdown=layui.dropdown,select={config:{valueSeparator:",",keywordPlaceholder:"请输入关键词",unfilteredText:"没有匹配的选项",customName:{id:"id",title:"title",selected:"selected"},options:[],allowCreate:!0,collapseSelected:!1,url:void 0,parseOptions:void 0}},thisSelect=function(){var that=this;return{config:that.config,reload:function(config){that.reload.call(that,config)},val:function(value){if(void 0===value)return that.context.selectedIds.join(that.config.valueSeparator);that.clear();for(var ids=$.isArray(value)?value:value.split(that.config.valueSeparator),i=0;i0&&(width=layuiInputBlock.width());var layuiInputGroup=that.config.elem.parents(".layui-input-group");layuiInputGroup.length>0&&(width-=layuiInputGroup.width()),that.config.elem.wrap('
                '),that.config.elem.after('
                '),that.render()};Class.prototype.render=function(){var that=this;if(that.config.url)return void $.get(that.config.url,{},function(res){var options=[];options=that.config.parseOptions?that.config.parseOptions(res):res,that.doRender(options)});that.doRender(that.config.options)},Class.prototype.doRender=function(options){var that=this,initOptions=options.map(function(option){var opt={};return opt[that.config.customName.id]=option[that.config.customName.id],opt[that.config.customName.title]=option[that.config.customName.title],opt[that.config.customName.selected]=option[that.config.customName.selected],opt}),selectedIds=initOptions.filter(function(option){return option[that.config.customName.selected]}).map(function(option){return option[that.config.customName.id]});that.config.elem.val(selectedIds.join(that.config.valueSeparator)),that.config.elem.parent().on("click",".multiple-select-selection-item-remove",function(e){e.stopPropagation();var id=$(this).parent().data("id"),option=that.getOptionById(id);that.remove(option)}),that.context={keyword:"",filteredOptions:null,selectedIds:selectedIds,options:initOptions,dropdownMenuScrollTop:0},that.renderDropdown(),that.renderSelection()},Class.prototype.clear=function(){this.context.selectedIds=[],this.reloadDropdownData(this.buildRenderOptions())},Class.prototype.remove=function(option){var that=this;this.context.selectedIds=this.context.selectedIds.filter(function(id){return id!=option[that.config.customName.id]}),this.reloadDropdownData(this.buildRenderOptions())},Class.prototype.select=function(options){options=layui.isArray(options)?options:[options];for(var length=options.length,i=0;i
                ','
                  ',options.length>0?options.map(function(option){return'
                • '+option[that.config.customName.title]+"
                • "}).join(""):'
                  '+this.config.unfilteredText+"
                  ","
                "].join("")},Class.prototype.reloadDropdownData=function(options){options&&options.length===this.getAllOptions().length&&this.resetSearch();var renderOptions=this.buildRenderOptions(options);this.dropdown.reloadData({content:this.buildDropdownContent(renderOptions)}),this.renderSelection()},Class.prototype.getSearchInput=function(){if(this.panel)return this.panel.find("div.multiple-select-search > input")},Class.prototype.renderDropdown=function(){var that=this;that.dropdown=dropdown.render({elem:that.config.elem.parent(),style:"width:"+that.config.elem.outerWidth()+"px",content:that.buildDropdownContent(that.getAllOptions()),ready:function(panel,elem){function reloadDropdown(keyword){var filterOptions=that.filterOptions(keyword);if(0===filterOptions.length&&that.config.allowCreate){var opt={};opt[that.config.customName.id]=keyword,opt[that.config.customName.title]=keyword,filterOptions.push(opt)}that.context.keyword=keyword,that.context.filteredOptions=filterOptions,that.reloadDropdownData(that.context.filteredOptions)}that.panel=panel,elem.addClass("multiple-select-panel-opended"),that.context.dropdownMenuScrollTop>0&&panel.find(".layui-dropdown-menu").scrollTop(that.context.dropdownMenuScrollTop);var inputCompositionStart=!1,searchInput=that.getSearchInput();searchInput.on("input",function(){inputCompositionStart||reloadDropdown($(this).val())}).on("compositionstart",function(){inputCompositionStart=!0}).on("compositionend",function(){inputCompositionStart=!1,reloadDropdown($(this).val())}),""!==that.context.keyword&&searchInput.val(that.context.keyword).focus()},click:function(data,elem){if(!$(this).hasClass("layui-menu-item-none")){var value=elem.data("value"),option=that.getOptionById(value);if(!option){if(!that.config.allowCreate)return;var option={};option[that.config.customName.id]=value,option[that.config.customName.title]=value,option[that.config.customName.selected]=!0,that.context.options.push(option)}return that.context.dropdownMenuScrollTop=elem.parent().scrollTop(),-1===that.context.selectedIds.indexOf(option[that.config.customName.id])?that.select(option):that.remove(option),!1}},close:function(elem){that.context.keyword="",that.context.filteredOptions=null,that.reloadDropdownData(),elem.removeClass("multiple-select-panel-opended")}})},Class.prototype.getOptionById=function(id){var that=this;return this.getAllOptions().filter(function(option){return option[that.config.customName.id]==id})[0]},Class.prototype.renderSelection=function(){var that=this,inputWrap=that.config.elem.parent(),selectedOptions=that.getSelectedOptions(),options=selectedOptions.length>1&&that.config.collapseSelected?[selectedOptions[0]]:selectedOptions,selectionHtml="";selectedOptions.length>0&&(selectionHtml=laytpl('
                {{# layui.each(d.options, function(index, option) { }}
                {{= option[d.title] }}
                {{# }) }} {{# if (d.collapseSelected && d.total > 1) { }}
                + {{ d.total }}
                {{# } }}
                ').render({total:selectedOptions.length,options:options,id:that.config.customName.id,title:that.config.customName.title,collapseSelected:that.config.collapseSelected})),inputWrap.find(".multiple-select-selection").remove(),""!==selectionHtml&&inputWrap.append(selectionHtml),this.config.elem.val(this.context.selectedIds.join(this.context.valueSeparator))},Class.prototype.getSelectedOptions=function(){for(var selectedIds=this.context.selectedIds,selectedOptions=[],i=0;ie.length)&&(t=e.length);for(var n=0,o=new Array(t);n1&&void 0!==arguments[1]?arguments[1]:100,n=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],o=this,r=!1;return function(){for(var i=arguments.length,a=new Array(i),l=0;l>>0;for(t&&(n=t),o=new Array(a),r=0;r>>0;if("function"!=typeof e)throw new TypeError(e+" is not a function");for(arguments.length>1&&(n=t),o=0;o>>0;if("function"!=typeof e)throw new TypeError;for(var o=[],r=arguments[1],i=0;i>>0,r=arguments[1],i=0;i .xm-tips {\n color: #999999;\n padding: 0 10px;\n position: absolute;\n display: flex;\n height: 100%;\n align-items: center;\n}\nxm-select > .xm-icon {\n display: inline-block;\n overflow: hidden;\n position: absolute;\n width: 0;\n height: 0;\n right: 10px;\n top: 50%;\n margin-top: -3px;\n cursor: pointer;\n border: 6px dashed transparent;\n border-top-color: #C2C2C2;\n border-top-style: solid;\n transition: all 0.3s;\n -webkit-transition: all 0.3s;\n}\nxm-select > .xm-icon-expand {\n margin-top: -9px;\n transform: rotate(180deg);\n}\nxm-select > .xm-label.single-row {\n position: absolute;\n top: 0;\n bottom: 0px;\n left: 0px;\n right: 30px;\n overflow: auto hidden;\n}\nxm-select > .xm-label.single-row .scroll {\n overflow-y: hidden;\n}\nxm-select > .xm-label.single-row .label-content {\n flex-wrap: nowrap;\n white-space: nowrap;\n}\nxm-select > .xm-label.auto-row .label-content {\n flex-wrap: wrap;\n padding-right: 30px !important;\n}\nxm-select > .xm-label.auto-row .xm-label-block > span {\n white-space: unset;\n height: 100%;\n}\nxm-select > .xm-label .scroll .label-content {\n display: flex;\n padding: 3px 10px;\n}\nxm-select > .xm-label .xm-label-block {\n display: flex;\n position: relative;\n padding: 0px 5px;\n margin: 2px 5px 2px 0;\n border-radius: 3px;\n align-items: baseline;\n color: #FFF;\n}\nxm-select > .xm-label .xm-label-block > span {\n display: flex;\n color: #FFF;\n white-space: nowrap;\n}\nxm-select > .xm-label .xm-label-block > i {\n color: #FFF;\n margin-left: 8px;\n font-size: 12px;\n cursor: pointer;\n display: flex;\n}\nxm-select > .xm-label .xm-label-block.disabled {\n background-color: #C2C2C2 !important;\n cursor: no-drop !important;\n}\nxm-select > .xm-label .xm-label-block.disabled > i {\n cursor: no-drop !important;\n}\nxm-select > .xm-body {\n position: absolute;\n left: 0;\n top: 42px;\n padding: 5px 0;\n z-index: 999;\n width: 100%;\n min-width: fit-content;\n border: 1px solid #E6E6E6;\n background-color: #fff;\n border-radius: 2px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12);\n animation-name: xm-upbit;\n animation-duration: 0.3s;\n animation-fill-mode: both;\n}\nxm-select > .xm-body .scroll-body {\n overflow-x: hidden;\n overflow-y: auto;\n}\nxm-select > .xm-body .scroll-body::-webkit-scrollbar {\n width: 8px;\n}\nxm-select > .xm-body .scroll-body::-webkit-scrollbar-track {\n -webkit-border-radius: 2em;\n -moz-border-radius: 2em;\n -ms-border-radius: 2em;\n border-radius: 2em;\n background-color: #FFF;\n}\nxm-select > .xm-body .scroll-body::-webkit-scrollbar-thumb {\n -webkit-border-radius: 2em;\n -moz-border-radius: 2em;\n -ms-border-radius: 2em;\n border-radius: 2em;\n background-color: #C2C2C2;\n}\nxm-select > .xm-body.up {\n top: auto;\n bottom: 42px;\n}\nxm-select > .xm-body.relative {\n position: relative;\n display: block !important;\n top: 0;\n box-shadow: none;\n border: none;\n animation-name: none;\n animation-duration: 0;\n min-width: 100%;\n}\nxm-select > .xm-body .xm-group {\n cursor: default;\n}\nxm-select > .xm-body .xm-group-item {\n display: inline-block;\n cursor: pointer;\n padding: 0 10px;\n color: #999;\n font-size: 12px;\n}\nxm-select > .xm-body .xm-option {\n display: flex;\n align-items: center;\n position: relative;\n padding: 0 10px;\n cursor: pointer;\n}\nxm-select > .xm-body .xm-option-icon {\n color: transparent;\n display: flex;\n border: 1px solid #E6E6E6;\n border-radius: 3px;\n justify-content: center;\n align-items: center;\n}\nxm-select > .xm-body .xm-option-icon.xm-custom-icon {\n color: unset;\n border: unset;\n}\nxm-select > .xm-body .xm-option-icon-hidden {\n margin-right: -10px;\n}\nxm-select > .xm-body .xm-option-icon.xm-icon-danx {\n border-radius: 100%;\n}\nxm-select > .xm-body .xm-option-content {\n display: flex;\n position: relative;\n padding-left: 15px;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n color: #666;\n width: calc(100% - 20px);\n}\nxm-select > .xm-body .xm-option.hide-icon .xm-option-content {\n padding-left: 0;\n}\nxm-select > .xm-body .xm-option.selected.hide-icon .xm-option-content {\n color: #FFF !important;\n}\nxm-select > .xm-body .xm-option .loader {\n width: 0.8em;\n height: 0.8em;\n margin-right: 6px;\n color: #C2C2C2;\n}\nxm-select > .xm-body .xm-select-empty {\n text-align: center;\n color: #999;\n}\nxm-select > .xm-body .disabled {\n cursor: no-drop;\n}\nxm-select > .xm-body .disabled:hover {\n background-color: #FFF;\n}\nxm-select > .xm-body .disabled .xm-option-icon {\n border-color: #C2C2C2 !important;\n}\nxm-select > .xm-body .disabled .xm-option-content {\n color: #C2C2C2 !important;\n}\nxm-select > .xm-body .disabled.selected > .xm-option-icon {\n color: #C2C2C2 !important;\n}\nxm-select > .xm-body .xm-search {\n background-color: #FFF !important;\n position: relative;\n padding: 0 10px;\n margin-bottom: 5px;\n cursor: pointer;\n}\nxm-select > .xm-body .xm-search > i {\n position: absolute;\n color: #666;\n}\nxm-select > .xm-body .xm-search-input {\n border: none;\n border-bottom: 1px solid #E6E6E6;\n padding-left: 27px;\n cursor: text;\n}\nxm-select > .xm-body .xm-paging {\n padding: 0 10px;\n display: flex;\n margin-top: 5px;\n}\nxm-select > .xm-body .xm-paging > span:first-child {\n border-radius: 2px 0 0 2px;\n}\nxm-select > .xm-body .xm-paging > span:last-child {\n border-radius: 0 2px 2px 0;\n}\nxm-select > .xm-body .xm-paging > span {\n display: flex;\n flex: auto;\n justify-content: center;\n vertical-align: middle;\n margin: 0 -1px 0 0;\n background-color: #fff;\n color: #333;\n font-size: 12px;\n border: 1px solid #e2e2e2;\n flex-wrap: nowrap;\n width: 100%;\n overflow: hidden;\n min-width: 50px;\n}\nxm-select > .xm-body .xm-toolbar {\n padding: 0 10px;\n display: flex;\n margin: -3px 0;\n cursor: default;\n}\nxm-select > .xm-body .xm-toolbar .toolbar-tag {\n cursor: pointer;\n display: flex;\n margin-right: 15px;\n color: #666;\n align-items: baseline;\n}\nxm-select > .xm-body .xm-toolbar .toolbar-tag:hover {\n opacity: 0.8;\n}\nxm-select > .xm-body .xm-toolbar .toolbar-tag:active {\n opacity: 1;\n}\nxm-select > .xm-body .xm-toolbar .toolbar-tag > i {\n margin-right: 2px;\n font-size: 14px;\n}\nxm-select > .xm-body .xm-toolbar .toolbar-tag:last-child {\n margin-right: 0;\n}\nxm-select > .xm-body .xm-body-custom {\n line-height: initial;\n cursor: default;\n}\nxm-select > .xm-body .xm-body-custom * {\n box-sizing: initial;\n}\nxm-select > .xm-body .xm-tree {\n position: relative;\n}\nxm-select > .xm-body .xm-tree-icon {\n display: inline-block;\n margin-right: 3px;\n cursor: pointer;\n border: 6px dashed transparent;\n border-left-color: #C2C2C2;\n border-left-style: solid;\n transition: all 0.3s;\n -webkit-transition: all 0.3s;\n z-index: 2;\n visibility: hidden;\n}\nxm-select > .xm-body .xm-tree-icon.expand {\n margin-top: 3px;\n margin-right: 5px;\n margin-left: -2px;\n transform: rotate(90deg);\n}\nxm-select > .xm-body .xm-tree-icon.xm-visible {\n visibility: visible;\n}\nxm-select > .xm-body .xm-tree .left-line {\n position: absolute;\n left: 13px;\n width: 0;\n z-index: 1;\n border-left: 1px dotted #c0c4cc !important;\n}\nxm-select > .xm-body .xm-tree .top-line {\n position: absolute;\n left: 13px;\n height: 0;\n z-index: 1;\n border-top: 1px dotted #c0c4cc !important;\n}\nxm-select > .xm-body .xm-tree .xm-tree-icon + .top-line {\n margin-left: 1px;\n}\nxm-select > .xm-body .scroll-body > .xm-tree > .xm-option > .top-line,\nxm-select > .xm-body .scroll-body > .xm-option > .top-line {\n width: 0 !important;\n}\nxm-select > .xm-body .xm-cascader-box {\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n padding: 5px 0;\n border: 1px solid #E6E6E6;\n background-color: #fff;\n border-radius: 2px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12);\n margin: -1px;\n}\nxm-select > .xm-body .xm-cascader-box::before {\n content: ' ';\n position: absolute;\n width: 0;\n height: 0;\n border: 6px solid transparent;\n border-right-color: #E6E6E6;\n top: 10px;\n left: -12px;\n}\nxm-select > .xm-body .xm-cascader-box::after {\n content: ' ';\n position: absolute;\n width: 0;\n height: 0;\n border: 6px solid transparent;\n border-right-color: #fff;\n top: 10px;\n left: -11px;\n}\nxm-select > .xm-body .xm-cascader-scroll {\n height: 100%;\n overflow-x: hidden;\n overflow-y: auto;\n}\nxm-select > .xm-body.cascader {\n width: unset;\n min-width: unset;\n}\nxm-select > .xm-body.cascader .xm-option-content {\n padding-left: 8px;\n}\nxm-select > .xm-body.cascader .disabled .xm-right-arrow {\n color: #C2C2C2 !important;\n}\nxm-select > .xm-body.cascader .hide-icon.disabled .xm-right-arrow {\n color: #999 !important;\n}\nxm-select .xm-input {\n cursor: pointer;\n border-radius: 2px;\n border-width: 1px;\n border-style: solid;\n border-color: #E6E6E6;\n display: block;\n width: 100%;\n box-sizing: border-box;\n background-color: #FFF;\n line-height: 1.3;\n padding-left: 10px;\n outline: 0;\n user-select: text;\n -ms-user-select: text;\n -moz-user-select: text;\n -webkit-user-select: text;\n}\nxm-select .dis {\n display: none;\n}\nxm-select .loading {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(255, 255, 255, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n}\nxm-select .loader {\n border: 0.2em dotted currentcolor;\n border-radius: 50%;\n -webkit-animation: 1s loader linear infinite;\n animation: 1s loader linear infinite;\n display: inline-block;\n width: 1em;\n height: 1em;\n color: inherit;\n vertical-align: middle;\n pointer-events: none;\n}\nxm-select .xm-select-default {\n position: absolute;\n width: 100%;\n height: 100%;\n border: none;\n visibility: hidden;\n}\nxm-select .xm-select-disabled {\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n cursor: no-drop;\n z-index: 2;\n opacity: 0.3;\n background-color: #FFF;\n}\nxm-select .item--divided {\n border-top: 1px solid #ebeef5;\n width: calc(100% - 20px);\n cursor: initial;\n}\nxm-select .xm-right-arrow {\n position: absolute;\n color: #666;\n right: 5px;\n top: -1px;\n font-weight: 700;\n transform: scale(0.6, 1);\n}\nxm-select .xm-right-arrow::after {\n content: '>';\n}\nxm-select[size='large'] {\n min-height: 40px;\n line-height: 40px;\n}\nxm-select[size='large'] .xm-input {\n height: 40px;\n}\nxm-select[size='large'] .xm-label .scroll .label-content {\n line-height: 34px;\n}\nxm-select[size='large'] .xm-label .xm-label-block {\n height: 30px;\n line-height: 30px;\n}\nxm-select[size='large'] .xm-body .xm-option .xm-option-icon {\n height: 20px;\n width: 20px;\n font-size: 20px;\n}\nxm-select[size='large'] .xm-paging > span {\n height: 34px;\n line-height: 34px;\n}\nxm-select[size='large'] .xm-tree .left-line {\n height: 100%;\n bottom: 20px;\n}\nxm-select[size='large'] .xm-tree .left-line-group {\n height: calc(100% - 40px);\n}\nxm-select[size='large'] .xm-tree .xm-tree-icon.xm-hidden + .top-line {\n top: 19px;\n}\nxm-select[size='large'] .item--divided {\n margin: 10px;\n}\nxm-select {\n min-height: 36px;\n line-height: 36px;\n}\nxm-select .xm-input {\n height: 36px;\n}\nxm-select .xm-label .scroll .label-content {\n line-height: 30px;\n}\nxm-select .xm-label .xm-label-block {\n height: 26px;\n line-height: 26px;\n}\nxm-select .xm-body .xm-option .xm-option-icon {\n height: 18px;\n width: 18px;\n font-size: 18px;\n}\nxm-select .xm-paging > span {\n height: 30px;\n line-height: 30px;\n}\nxm-select .xm-tree .left-line {\n height: 100%;\n bottom: 18px;\n}\nxm-select .xm-tree .left-line-group {\n height: calc(100% - 36px);\n}\nxm-select .xm-tree .xm-tree-icon.xm-hidden + .top-line {\n top: 17px;\n}\nxm-select .item--divided {\n margin: 9px;\n}\nxm-select[size='small'] {\n min-height: 32px;\n line-height: 32px;\n}\nxm-select[size='small'] .xm-input {\n height: 32px;\n}\nxm-select[size='small'] .xm-label .scroll .label-content {\n line-height: 26px;\n}\nxm-select[size='small'] .xm-label .xm-label-block {\n height: 22px;\n line-height: 22px;\n}\nxm-select[size='small'] .xm-body .xm-option .xm-option-icon {\n height: 16px;\n width: 16px;\n font-size: 16px;\n}\nxm-select[size='small'] .xm-paging > span {\n height: 26px;\n line-height: 26px;\n}\nxm-select[size='small'] .xm-tree .left-line {\n height: 100%;\n bottom: 16px;\n}\nxm-select[size='small'] .xm-tree .left-line-group {\n height: calc(100% - 32px);\n}\nxm-select[size='small'] .xm-tree .xm-tree-icon.xm-hidden + .top-line {\n top: 15px;\n}\nxm-select[size='small'] .item--divided {\n margin: 8px;\n}\nxm-select[size='mini'] {\n min-height: 28px;\n line-height: 28px;\n}\nxm-select[size='mini'] .xm-input {\n height: 28px;\n}\nxm-select[size='mini'] .xm-label .scroll .label-content {\n line-height: 22px;\n}\nxm-select[size='mini'] .xm-label .xm-label-block {\n height: 18px;\n line-height: 18px;\n}\nxm-select[size='mini'] .xm-body .xm-option .xm-option-icon {\n height: 14px;\n width: 14px;\n font-size: 14px;\n}\nxm-select[size='mini'] .xm-paging > span {\n height: 22px;\n line-height: 22px;\n}\nxm-select[size='mini'] .xm-tree .left-line {\n height: 100%;\n bottom: 14px;\n}\nxm-select[size='mini'] .xm-tree .left-line-group {\n height: calc(100% - 28px);\n}\nxm-select[size='mini'] .xm-tree .xm-tree-icon.xm-hidden + .top-line {\n top: 13px;\n}\nxm-select[size='mini'] .item--divided {\n margin: 7px;\n}\n.layui-form-pane xm-select {\n margin: -1px -1px -1px 0;\n}\n",""]),e.exports=t},221:function(e,t){(function(t){e.exports=t}).call(this,{})},27:function(e,t,n){ "use strict";e.exports=function(e){var t=[];return t.toString=function(){return this.map(function(t){var n=function(e,t){var n=e[1]||"",o=e[3];if(!o)return n;if(t&&"function"==typeof btoa){var r=function(e){return"/*# ".concat("sourceMappingURL=data:application/json;charset=utf-8;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(e)))))," */")}(o);return[n].concat(o.sources.map(function(e){return"/*# sourceURL=".concat(o.sourceRoot||"").concat(e," */")})).concat([r]).join("\n")}return[n].join("\n")}(t,e);return t[2]?"@media ".concat(t[2]," {").concat(n,"}"):n}).join("")},t.i=function(e,n,o){"string"==typeof e&&(e=[[null,e,""]]);var r={};if(o)for(var i=0;i=0&&p.splice(t,1)}function y(e){var t=document.createElement("style");if(void 0===e.attrs.type&&(e.attrs.type="text/css"),void 0===e.attrs.nonce){var o=function(){return n.nc}();o&&(e.attrs.nonce=o)}return x(t,e.attrs),m(e,t),t}function x(e,t){Object.keys(t).forEach(function(n){e.setAttribute(n,t[n])})}function v(e,t){var n,o,r,i;if(t.transform&&e.css){if(!(i="function"==typeof t.transform?t.transform(e.css):t.transform.default(e.css)))return function(){};e.css=i}if(t.singleton){var a=u++;n=c||(c=y(t)),o=w.bind(null,n,a,!1),r=w.bind(null,n,a,!0)}else e.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(n=function(e){var t=document.createElement("link");return void 0===e.attrs.type&&(e.attrs.type="text/css"),e.attrs.rel="stylesheet",x(t,e.attrs),m(e,t),t}(t),o=O.bind(null,n,t),r=function(){b(n),n.href&&URL.revokeObjectURL(n.href)}):(n=y(t),o=k.bind(null,n),r=function(){b(n)});return o(e),function(t){if(t){if(t.css===e.css&&t.media===e.media&&t.sourceMap===e.sourceMap)return;o(e=t)}else r()}}function w(e,t,n,o){var r=n?"":o.css;if(e.styleSheet)e.styleSheet.cssText=_(t,r);else{var i=document.createTextNode(r),a=e.childNodes;a[t]&&e.removeChild(a[t]),a.length?e.insertBefore(i,a[t]):e.appendChild(i)}}function k(e,t){var n=t.css,o=t.media;if(o&&e.setAttribute("media",o),e.styleSheet)e.styleSheet.cssText=n;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(n))}}function O(e,t,n){var o=n.css,r=n.sourceMap,i=void 0===t.convertToAbsoluteUrls&&r;(t.convertToAbsoluteUrls||i)&&(o=f(o)),r&&(o+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(r))))+" */");var a=new Blob([o],{type:"text/css"}),l=e.href;e.href=URL.createObjectURL(a),l&&URL.revokeObjectURL(l)}var o,r,i={},a=(o=function(){return window&&document&&document.all&&!window.atob},function(){return void 0===r&&(r=o.apply(this,arguments)),r}),l=function(e,t){return t?t.querySelector(e):document.querySelector(e)},s=function(e){var t={};return function(e,n){if("function"==typeof e)return e();if(void 0===t[e]){var o=l.call(this,e,n);if(window.HTMLIFrameElement&&o instanceof window.HTMLIFrameElement)try{o=o.contentDocument.head}catch(e){o=null}t[e]=o}return t[e]}}(),c=null,u=0,p=[],f=n(106);e.exports=function(e,t){(t=t||{}).attrs="object"==typeof t.attrs?t.attrs:{},t.singleton||"boolean"==typeof t.singleton||(t.singleton=a()),t.insertInto||(t.insertInto="head"),t.insertAt||(t.insertAt="bottom");var n=h(e,t);return d(n,t),function(e){for(var o=[],r=0;r3)for(n=[n],o=3;o1&&E(r,t,n),t=O(n,r,r,e.__k,null,r.__e,t),"function"==typeof e.type&&(e.__d=t)))}function A(e,t,n,r,i,a,l,s,c){var u,p,f,h,m,b,v,g,_,w,O,C=t.type;if(void 0!==t.constructor)return null;(u=o.__b)&&u(t);try{e:if("function"==typeof C){if(g=t.props,_=(u=C.contextType)&&r[u.__c],w=u?_?_.props.value:u.__:r,n.__c?v=(p=t.__c=n.__c).__=p.__E:("prototype"in C&&C.prototype.render?t.__c=p=new C(g,w):(t.__c=p=new x(g,w),p.constructor=C,p.render=M),_&&_.sub(p),p.props=g,p.state||(p.state={}),p.context=w,p.__n=r,f=p.__d=!0,p.__h=[]),null==p.__s&&(p.__s=p.state),null!=C.getDerivedStateFromProps&&(p.__s==p.state&&(p.__s=d({},p.__s)),d(p.__s,C.getDerivedStateFromProps(g,p.__s))),h=p.props,m=p.state,f)null==C.getDerivedStateFromProps&&null!=p.componentWillMount&&p.componentWillMount(),null!=p.componentDidMount&&p.__h.push(p.componentDidMount);else{if(null==C.getDerivedStateFromProps&&g!==h&&null!=p.componentWillReceiveProps&&p.componentWillReceiveProps(g,w),!p.__e&&null!=p.shouldComponentUpdate&&!1===p.shouldComponentUpdate(g,p.__s,w)||t.__v===n.__v){p.props=g,p.state=p.__s,t.__v!==n.__v&&(p.__d=!1),p.__v=t,t.__e=n.__e,t.__k=n.__k,p.__h.length&&l.push(p),E(t,s,e);break e}null!=p.componentWillUpdate&&p.componentWillUpdate(g,p.__s,w),null!=p.componentDidUpdate&&p.__h.push(function(){p.componentDidUpdate(h,m,b)})}p.context=w,p.props=g,p.state=p.__s,(u=o.__r)&&u(t),p.__d=!1,p.__v=t,p.__P=e,u=p.render(p.props,p.state,p.context),p.state=p.__s,null!=p.getChildContext&&(r=d(d({},r),p.getChildContext())),f||null==p.getSnapshotBeforeUpdate||(b=p.getSnapshotBeforeUpdate(h,m)),O=null!=u&&u.type==y&&null==u.key?u.props.children:u,k(e,Array.isArray(O)?O:[O],t,n,r,i,a,l,s,c),p.base=t.__e,p.__h.length&&l.push(p),v&&(p.__E=p.__=null),p.__e=!1}else null==a&&t.__v===n.__v?(t.__k=n.__k,t.__e=n.__e):t.__e=P(n.__e,t,n,r,i,a,l,c);(u=o.diffed)&&u(t)}catch(e){t.__v=null,o.__e(e,t,n)}return t.__e}function R(e,t){o.__c&&o.__c(t,e),e.some(function(t){try{e=t.__h,t.__h=[],e.some(function(e){e.call(t)})}catch(e){o.__e(e,t.__v)}})}function P(e,t,n,o,r,i,a,l){var s,c,f,d,h,m=n.props,b=t.props;if(r="svg"===t.type||r,null!=i)for(s=0;se.length)&&(t=e.length);for(var n=0,o=new Array(t);ne.length)&&(t=e.length);for(var n=0,o=new Array(t);ne.length)&&(t=e.length);for(var n=0,o=new Array(t);ne.length)&&(t=e.length);for(var n=0,o=new Array(t);nr?n-r:r,a=this.labelRef.scrollLeft+e.deltaY;a<0&&(a=0),a>i&&(a=i),this.labelRef.scrollLeft=a}}},{key:"blur",value:function(){var e=this.base.querySelector(".label-search-input");e&&e.blur()}},{key:"labelDrag",value:function(e,t){for(var n=t.type,o=t.target;;){if(!o||"I"===o.tagName)return;if("DIV"===o.tagName&&"fixed"!==o.style.position)break;o=o.parentNode}if(console.log(t),"mousedown"===n){var r=o.cloneNode(!0),i=t.pageX,a=t.pageY,l=t.offsetX,s=t.offsetY;console.log(i,a,l,s),r.style.position="fixed",r.style.left=i-l+"px",r.style.top=a-s+"px",o.appendChild(r),console.log(r),r.onmousemove=function(e){r.style.left=e.pageX-l+"px",r.style.top=e.pageY-s+"px"},r.mouseup=function(){r.parentNode.removeChild(r),r.onmousemove=null,r.mouseup=null,r.mouseleave=null},r.mouseleave=function(){console.log("mouseleave")}}else if("mouseup"===n)for(var c=o.childNodes,u=0;u0&&void 0!==arguments[0]||this.size;var e=this.state.pageIndex;e<=1||(this.changePageIndex(e-1),this.props.pageRemote&&this.postData(e-1,!0))}},{key:"pageNextClick",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.size,t=this.state.pageIndex;t>=e||(this.changePageIndex(t+1),this.props.pageRemote&&this.postData(t+1,!0))}},{key:"changePageIndex",value:function(e){this.setState({pageIndex:e})}},{key:"labelSearch",value:function(e){"input"==e.type?this.searchInput(e):this.handleComposition(e)}},{key:"searchInput",value:function(e){var t=this,n=e.target.value;n!==this.__value&&(this.searchCid&&clearTimeout(this.searchCid),this.inputOver&&(this.__value=n,this.searchCid=setTimeout(function(){t.callback=!0,t.setState({filterValue:t.__value,remote:!0,pageIndex:1})},this.props.delay)))}},{key:"focus",value:function(){this.searchInputRef&&this.searchInputRef.focus()}},{key:"blur",value:function(){this.searchInputRef&&this.searchInputRef.blur()}},{key:"handleComposition",value:function(e){var t=e.type;"compositionstart"===t?(this.inputOver=!1,this.searchCid&&clearTimeout(this.searchCid)):"compositionend"===t&&(this.inputOver=!0,this.searchInput(e))}},{key:"postData",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.state.pageIndex,n=arguments.length>1&&void 0!==arguments[1]&&arguments[1];(this.state.remote||n)&&(this.callback=!1,this.setState({loading:!0,remote:!1}),this.blur(),this.props.remoteMethod(this.state.filterValue,function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:1;setTimeout(function(){e.focus(),e.callback=!0,e.setState({loading:!1,totalSize:n}),e.props.onReset(t,"data")},10)},this.props.show,t))}},{key:"keydown",value:function(e,t){var n=this,o=t.keyCode;if("div"===e&&(27===o||9===o?this.props.onReset(!1,"close"):37===o?this.pagePrevClick():39===o&&this.pageNextClick()),this.props.enableKeyboard){var r=this.props.prop,i=r.value,a=r.optgroup,l=r.disabled,s=this.tempData.filter(function(e){return!e[a]&&!e[l]}),c=s.length-1;if(-1===c)return;var u=s.findIndex(function(e){return e[i]===n.state.val});if(38===o){u<=0?u=c:u>0&&(u-=1);var p=s[u][i];this.setState({val:p}),this.viewTo(p)}else if(40===o){-1===u||u===c?u=0:uM&&(T=M),M>0&&T<=0&&(T=1),!x){var z=(T-1)*e.pageSize,L=z+e.pageSize;S=S.slice(z,L)}var V={cursor:"no-drop",color:"#d2d2d2"},F={},U={};T<=1&&(F=V),T==M&&(U=V),this.state.pageIndex!==T&&this.changePageIndex(T),this.size=M,D=m("div",{class:"xm-paging"},m("span",{style:F,onClick:this.pagePrevClick.bind(this,M)},e.languageProp.paging.prev),m("span",null,this.state.pageIndex," / ",M),m("span",{style:U,onClick:this.pageNextClick.bind(this,M)},e.languageProp.paging.next))}else e.showCount>0&&(S=S.slice(0,e.showCount));var N,B,K=[],H={__tmp:!0};H[j]=!0,S.forEach(function(e){var t=I[e.__group__index];delete e.__group__index,N&&!t&&(t=H),t!=N&&(N=t,t&&K.push(N)),K.push(e)}),S=K,t&&null!=(t=y(this.state.filterValue,Object(c.b)([],S)))&&(B=S).splice.apply(B,[0,0].concat(J((Object(c.d)(t)?t:[t]).map(function(e){return Z(Z({},e),{},{__node:{}})}))));var q=Object(c.b)([],S);this.tempData=q;var Y=m("div",{class:"xm-toolbar"},e.toolbar.list.map(function(t){var o,r=e.languageProp.toolbar[t];o="ALL"===t?{icon:"xm-iconfont xm-icon-quanxuan",name:r,method:function(e){var t=i.optgroup,o=i.disabled,r=e.filter(function(e){return!e[t]}).filter(function(e){return!e[o]}),a=u.filter(function(e){return e[i.disabled]}),l=[];l=s?a.length?a:r.slice(0,1):v>0?a.length>=v?a:Object(c.f)(r.slice(0,v-a.length),a,i):Object(c.f)(r,u,i),n.props.onReset(l,"sels")}}:"CLEAR"===t?{icon:"xm-iconfont xm-icon-qingkong",name:r,method:function(e){n.props.onReset(u.filter(function(e){return e[i.disabled]}),"sels")}}:"REVERSE"===t?{icon:"xm-iconfont xm-icon-fanxuan",name:r,method:function(e){var t=i.optgroup,o=i.disabled,r=e.filter(function(e){return!e[t]}).filter(function(e){return!e[o]}),a=[];u.forEach(function(e){var t=r.findIndex(function(t){return t[k]===e[k]});-1==t?a.push(e):r.splice(t,1)});var l=a.filter(function(e){return e[i.disabled]}),p=[];p=s?l.length?l:r.slice(0,1):v>0?l.length>=v?l:Object(c.f)(r.slice(0,v-l.length),l,i):Object(c.f)(r,a,i),n.props.onReset(p,"sels")}}:t;var a=function(e){"mouseenter"===e.type&&(e.target.style.color=l.color),"mouseleave"===e.type&&(e.target.style.color="")};return m("div",{class:"toolbar-tag",style:{},onClick:function(){Object(c.e)(o.method)&&o.method(q),n.focus()},onMouseEnter:a,onMouseLeave:a},e.toolbar.showIcon&&m("i",{class:o.icon}),m("span",null,o.name))}).filter(function(e){return e})),Q="hidden"!=e.model.icon;return(S=S.map(function(t){return t[j]?t.__tmp?m("div",{class:"item--divided"}):m("div",{class:"xm-group"},m("div",{class:"xm-group-item",onClick:n.groupClick.bind(n,t)},t[w])):function(t){var r=!!u.find(function(e){return e[k]==t[k]}),i=r?{color:l.color,border:"none"}:{borderColor:l.color},c={};g&&t[k]===n.state.val&&(c.backgroundColor=l.hover),!Q&&r&&(c.backgroundColor=l.color,t[O]&&(c.backgroundColor="#C2C2C2"));var p,f,d=["xm-option",t[O]?" disabled":"",r?" selected":"",Q?"show-icon":"hide-icon"].join(" "),h=["xm-option-icon",(p=e.iconfont.select,f=e.iconfont.unselect,(p?!r&&f?f+" xm-custom-icon":p:0)||"xm-iconfont "+(s?"xm-icon-danx":"xm-icon-duox"))].join(" "),b=function(e){"mouseenter"===e.type?t[O]||(g?n.setState({val:t[k]}):e.target.style.backgroundColor=l.hover):"mouseleave"===e.type&&(t[O]||g||(e.target.style.backgroundColor=""))};return m("div",{class:d,style:c,value:t[k],onClick:n.optionClick.bind(n,t,r,t[O]),onMouseEnter:b,onMouseLeave:b},Q&&m("i",{class:h,style:i}),m("div",{class:"xm-option-content",dangerouslySetInnerHTML:{__html:a({data:o,item:t,arr:u,name:t[w],value:t[k]})}}))}(t)})).length?_&&this.state.val==ne&&this.keydown("div",{keyCode:40}):(!e.pageEmptyShow&&(D=""),S.push(m("div",{class:"xm-select-empty"},p))),m("div",{onClick:this.blockClick,tabindex:"1",style:"outline: none;"},m("div",null,e.toolbar.show&&Y,P,m("div",{class:"scroll-body",style:{maxHeight:e.height}},S),e.paging&&D),this.state.loading&&m("div",{class:"loading"},m("span",{class:"loader"})))}},{key:"componentDidMount",value:function(){var e=this.base.querySelector(".xm-search-input");e&&(e.addEventListener("compositionstart",this.handleComposition.bind(this)),e.addEventListener("compositionupdate",this.handleComposition.bind(this)),e.addEventListener("compositionend",this.handleComposition.bind(this)),e.addEventListener("input",this.searchInput.bind(this)),this.searchInputRef=e),this.base.addEventListener("keydown",this.keydown.bind(this,"div"))}},{key:"componentDidUpdate",value:function(){if(this.callback){this.callback=!1;var e=this.props.filterDone;Object(c.e)(e)&&e(this.state.filterValue,this.tempData||[])}}}])&&G(t.prototype,n),i}(x),ue=function(e){function i(e){return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,i),r.call(this,e)}!function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&ae(e,t)}(i,e);var t,n,r=le(i);return t=i,(n=[{key:"blockClick",value:function(e){e.stopPropagation()}},{key:"shouldComponentUpdate",value:function(){return!this.prepare}},{key:"render",value:function(e){return this.prepare=!0,m("div",{onClick:this.blockClick,class:"xm-body-custom"},m("div",{class:"scroll-body",style:{maxHeight:e.height}},m("div",{style:"margin: 5px 0",dangerouslySetInnerHTML:{__html:e.content}})))}}])&&ie(t.prototype,n),i}(x),ye={},xe=function(e){function i(e){var t;return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,i),(t=r.call(this,e)).state={expandedKeys:[],filterValue:"",remote:!0,loading:!1,val:ye},t.searchCid=0,t.inputOver=!0,t.__value="",t.tempData=[],t.__skipAutoExpand="",t}!function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&de(e,t)}(i,e);var t,n,r=he(i);return t=i,(n=[{key:"init",value:function(e){var t=e.tree,n=e.dataObj,o=e.flatData,r=e.prop,i=r.value,a=r.optgroup,l=[];!0===t.expandedKeys?l=o.filter(function(e){return!0===e[a]}).map(function(e){return e[i]}):!1===t.expandedKeys||t.expandedKeys.forEach(function(e){l.push(e);for(var t=n[e];t;)!function(){var e=t[i];-1===l.findIndex(function(t){return t===e})&&l.push(e),t=t.__node.parent}()}),this.setState({expandedKeys:l})}},{key:"blockClick",value:function(e){e.stopPropagation()}},{key:"optionClick",value:function(e,t,n,o,r){var i=this;if("line"===o){if(!0===e.__node.loading)return;var a=this.props,l=a.tree,s=a.prop,u=a.sels,p=l.clickExpand,f=l.clickCheck,d=r.target&&Object(c.e)(r.target.getAttribute)&&"expand"===r.target.getAttribute("type");if(p||d){if(!l.lazy&&!e[s.optgroup])return void this.props.ck(e,t,n);var h=e[this.props.prop.value],m=this.state.expandedKeys,b=m.findIndex(function(e){return e===h});-1===b?m.push(h):m.splice(b,1),this.setState({expandedKeys:m});var y=e[s.children];l.lazy&&y&&0===y.length&&!1!==e.__node.loading&&(e.__node.loading=!0,l.load(e,function(t){e.__node.loading=!1,e[s.children]=i.handlerData(t,s.children),e[s.selected]=-1!=u.findIndex(function(t){return t[s.value]===e[s.value]}),i.props.onReset(u,"treeData")}))}else f&&(o="checkbox")}"checkbox"===o&&this.props.ck(e,t,n),this.blockClick(r)}},{key:"handlerData",value:function(e,t){var n=this;return e.map(function(e){return e.__node={},e[t]&&(e[t]=n.handlerData(e[t],t)),e})}},{key:"searchInput",value:function(e){var t=this,n=e.target.value;n!==this.__value&&(clearTimeout(this.searchCid),this.inputOver&&(this.__value=n,this.searchCid=setTimeout(function(){t.callback=!0,t.setState({filterValue:t.__value,remote:!0})},this.props.delay)))}},{key:"focus",value:function(){this.searchInputRef&&this.searchInputRef.focus()}},{key:"blur",value:function(){this.searchInputRef&&this.searchInputRef.blur()}},{key:"handleComposition",value:function(e){var t=e.type;"compositionstart"===t?(this.inputOver=!1,clearTimeout(this.searchCid)):"compositionend"===t&&(this.inputOver=!0,this.searchInput(e))}},{key:"filterData",value:function(e,t,n){var o=this,r=this.props,i=r.prop,a=r.filterMethod,l=r.tree,s=i.children,c=i.optgroup,u=(i.name,i.value);return e.forEach(function(e,r){var p,f=!!t&&!a(t,e,r,i);if(l.strict?p=!1:f=p=!1!==n&&f,e[c]){var d=o.filterData(e[s],t,p),h=!!t&&0===d.filter(function(e){return!e.__node.hidn}).length;if(!(f=(l.strict||p)&&h)&&o.__skipAutoExpand!=t){var m=o.state.expandedKeys;t&&-1===m.findIndex(function(t){return t===e[u]})&&(m.push(e[u]),o.setState({expandedKeys:m})),o.__skipAutoExpand=t}}e.__node.hidn=f}),e}},{key:"postData",value:function(){var e=this;this.state.remote&&(this.callback=!1,this.setState({loading:!0,remote:!1}),this.blur(),this.props.remoteMethod(this.state.filterValue,function(t,n){e.focus(),e.callback=!0,e.setState({loading:!1,totalSize:n}),e.props.onReset(t,"data")},this.props.show,1))}},{key:"componentWillReceiveProps",value:function(e){var t=this;this.props.show!=e.show&&(e.show?setTimeout(function(){return t.focus()},0):(this.setState({filterValue:"",val:ye}),this.__value="",this.__skipAutoExpand="",this.searchInputRef&&(this.searchInputRef.value="")))}},{key:"componentWillMount",value:function(){this.init(this.props)}},{key:"render",value:function(e,t){function E(e,t){t.forEach(function(t){t[w]?(p.strict||"hidden"===b.parent||e.push(t),E(e,t[_])):e.push(t)})}var n=this,o=(t.expandedKeys,e.prop),r=e.empty,i=e.sels,a=e.theme,l=e.radio,s=e.template,u=e.data,p=e.tree,f=e.filterable,d=e.remoteSearch,h=e.searchTips,b=e.iconfont,y=e.enableKeyboard,x=o.name,v=o.value,g=o.disabled,_=o.children,w=o.optgroup,k="hidden"!=e.model.icon,O=function(e,t,o){var r=!!i.find(function(t){return t[v]==e[v]}),c=e[g],f=!0===e.__node.half;p.strict&&(r=r||f||e.__node.selected,c=c||e.__node.disabled);var d=r?{color:a.color,border:"none"}:{borderColor:a.color},h={paddingLeft:t+"px"};y&&e[v]===n.state.val&&(h.backgroundColor=a.hover),!k&&r&&(h.backgroundColor=a.color,c&&(h.backgroundColor="#C2C2C2"));var w=["xm-option",c?" disabled":"",r?" selected":"",k?"show-icon":"hide-icon"].join(" "),O=0!==o&&"hidden"===b.parent?"xm-option-icon-hidden":["xm-option-icon",(f?b.half?b.half+" xm-custom-icon":0:0!==o&&b.parent?b.parent+" xm-custom-icon":r?b.select?b.select:0:b.unselect?b.unselect+" xm-custom-icon":0)||"xm-iconfont "+(l?"xm-icon-danx":p.strict&&f?"xm-icon-banxuan":"xm-icon-duox")].join(" "),C=["xm-tree-icon",o?"expand":"",e[_]&&(e[_].length>0||p.lazy&&!1!==e.__node.loading)?"xm-visible":"xm-hidden"].join(" "),j=[];p.showFolderIcon&&(j.push(m("i",{class:C,type:"expand"})),p.showLine&&(o&&j.push(m("i",{class:"left-line",style:{left:t-p.indent+3+"px"}})),j.push(m("i",{class:"top-line",style:{left:t-p.indent+3+"px",width:p.indent+(0===o?10:-2)+"px"}}))));var S=function(t){"mouseenter"===t.type?e[g]||(y?n.setState({val:e[v]}):t.target.style.backgroundColor=a.hover):"mouseleave"===t.type&&(e[g]||y||(t.target.style.backgroundColor=""))};return m("div",{class:w,style:h,value:e[v],onClick:n.optionClick.bind(n,e,r,e[g],"line"),onMouseEnter:S,onMouseLeave:S},j,e.__node.loading&&m("span",{class:"loader"}),k&&m("i",{class:O,style:d,onClick:n.optionClick.bind(n,e,r,e[g],"checkbox")}),m("div",{class:"xm-option-content",dangerouslySetInnerHTML:{__html:s({data:u,item:e,arr:i,name:e[x],value:e[v]})}}))};f&&(d?this.postData():this.filterData(u,this.state.filterValue));var C=Object(c.b)([],u),j=Object(c.b)([],i);this.tempData=C;var S=u.map(function(e){return function e(t,o){if(!t.__node.hidn){var r=t[_];if(o+=p.indent,r){var i=-1!==n.state.expandedKeys.findIndex(function(e){return t[v]===e});return 0===r.length&&(i=!1),m("div",{class:"xm-tree"},p.showFolderIcon&&p.showLine&&i&&r.length>0&&m("i",{class:"left-line left-line-group",style:{left:o+3+"px"}}),O(t,o,0===r.length&&(!p.lazy||p.lazy&&!1===t.__node.loading)?0:i),i&&m("div",{class:"xm-tree-box"},r.map(function(t){return e(t,o)})))}return O(t,o,0)}}(e,10-p.indent)}).filter(function(e){return e}),A=m("div",{class:"xm-toolbar"},e.toolbar.list.map(function(t){var r,s=e.languageProp.toolbar[t];r="ALL"===t?{icon:"xm-iconfont xm-icon-quanxuan",name:s,method:function(e){var t=[];E(t,e),t=t.filter(function(e){return!e[g]&&!e.__node.hidn}),n.props.onReset(l?t.slice(0,1):Object(c.f)(t,i,o),"treeData")}}:"CLEAR"===t?{icon:"xm-iconfont xm-icon-qingkong",name:s,method:function(e){n.props.onReset(i.filter(function(e){return e[o.disabled]}),"treeData")}}:"REVERSE"===t?{icon:"xm-iconfont xm-icon-fanxuan",name:s,method:function(e){var t=[];E(t,e),t=t.filter(function(e){return!e[g]&&!e.__node.hidn});var r=[];i.forEach(function(e){var n=t.findIndex(function(t){return t[v]===e[v]});-1==n?r.push(e):t.splice(n,1)}),n.props.onReset(l?r.slice(0,1):Object(c.f)(t,r,o),"treeData")}}:t;var u=function(e){"mouseenter"===e.type&&(e.target.style.color=a.color),"mouseleave"===e.type&&(e.target.style.color="")};return m("div",{class:"toolbar-tag",onClick:function(){Object(c.e)(r.method)&&r.method(C,j)},onMouseEnter:u,onMouseLeave:u},e.toolbar.showIcon&&m("i",{class:r.icon}),m("span",null,r.name))}).filter(function(e){return e})),R=m("div",{class:f?"xm-search":"xm-search dis"},m("i",{class:"xm-iconfont xm-icon-sousuo"}),m("input",{class:"xm-input xm-search-input",placeholder:h}));return S.length||S.push(m("div",{class:"xm-select-empty"},r)),m("div",{onClick:this.blockClick,class:"xm-body-tree"},e.toolbar.show&&A,R,m("div",{class:"scroll-body",style:{maxHeight:e.height}},S),this.state.loading&&m("div",{class:"loading"},m("span",{class:"loader"})))}},{key:"componentDidMount",value:function(){var e=this.base.querySelector(".xm-search-input");e&&(e.addEventListener("compositionstart",this.handleComposition.bind(this)),e.addEventListener("compositionupdate",this.handleComposition.bind(this)),e.addEventListener("compositionend",this.handleComposition.bind(this)),e.addEventListener("input",this.searchInput.bind(this)),this.searchInputRef=e)}},{key:"componentDidUpdate",value:function(){if(this.callback){this.callback=!1;var e=this.props.filterDone;Object(c.e)(e)&&e(this.state.filterValue,this.tempData||[])}}}])&&fe(t.prototype,n),i}(x),Ce=function(e){function i(e){var t;return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,i),(t=r.call(this,e)).state={expand:[]},t}!function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&_e(e,t)}(i,e);var t,n,r=we(i);return t=i,(n=[{key:"blockClick",value:function(e){e.stopPropagation()}},{key:"optionClick",value:function(e,t,n,o,r,i){if("line"===o){if(!e.optgroup&&n)return;if(!0===e.__node.loading)return;var a=this.props,l=a.cascader,s=a.prop;if(a.sels,!l.lazy&&!e[s.optgroup])return void this.props.ck(e,t,n);var c=this.state.expand.slice(0,r+1);c[r]=e[this.props.prop.value],this.setState({expand:c})}else"checkbox"===o&&this.props.ck(e,t,n);this.blockClick(i)}},{key:"componentWillReceiveProps",value:function(e){}},{key:"componentWillMount",value:function(){}},{key:"render",value:function(e,t){var n=this,o=e.prop,r=e.empty,i=e.sels,a=e.theme,l=e.radio,s=e.template,c=e.data,u=e.cascader,p=o.name,f=o.value,d=o.disabled,h=o.children,b=o.optgroup,y="hidden"!=e.model.icon,x=[],v=function t(o,r,v){var g=o[h];r=r+u.indent+6;var _=g&&n.state.expand[v]===o[f];return _&&x.push(m("div",{class:"xm-cascader-box",index:v%4,style:{left:r+"px",width:u.indent+"px"}},m("div",{class:"xm-cascader-scroll scroll-body"},g.map(function(e){return t(e,r,v+1)})))),function(t,o,r,x){var v=!!i.find(function(e){return e[f]==t[f]}),g=t[d],_=!0===t.__node.half;u.strict&&(v=v||_||t.__node.selected,g=g||t.__node.disabled);var w=v?{color:a.color,border:"none"}:{borderColor:a.color},k=t[h]&&t[h].length>0,O={backgroundColor:"transparent"},C=["xm-option",g?" disabled":"",v?" selected":"",y?"show-icon":"hide-icon"].join(" "),j=k&&"hidden"===e.iconfont.parent?"xm-option-icon-hidden":["xm-option-icon",(_?e.iconfont.half?e.iconfont.half+" xm-custom-icon":0:k&&e.iconfont.parent?e.iconfont.parent+" xm-custom-icon":v?e.iconfont.select?e.iconfont.select:0:e.iconfont.unselect?e.iconfont.unselect+" xm-custom-icon":0)||"xm-iconfont "+(l?"xm-icon-danx":u.strict&&_?"xm-icon-banxuan":"xm-icon-duox")].join(" ");t[f]===n.state.val&&(O.backgroundColor=a.hover),!y&&v&&(O.backgroundColor=a.color,g&&(O.backgroundColor="#C2C2C2"));var S={},E={};x&&(S.color=a.color,S.fontWeight=700,E.color=a.color);var A=function(e){"mouseenter"===e.type?t[d]||n.setState({val:t[f]}):"mouseleave"===e.type&&n.setState({val:""})};return m("div",{class:C,style:O,value:t[f],onClick:n.optionClick.bind(n,t,v,g,"line",r),onMouseEnter:A,onMouseLeave:A},y&&m("i",{class:j,style:w,onClick:n.optionClick.bind(n,t,v,g,"checkbox",r)}),m("div",{class:"xm-option-content",style:S,dangerouslySetInnerHTML:{__html:s({data:c,item:t,arr:i,name:t[p],value:t[f]})}}),t[b]&&m("div",{class:"xm-right-arrow",style:E}))}(o,0,v,_)},g=c.map(function(e){return v(e,2,0)}).concat(x).filter(function(e){return e});return g.length||g.push(m("div",{class:"xm-select-empty"},r)),m("div",{onClick:this.blockClick,class:"xm-body-cascader scroll-body",style:{width:u.indent+"px",maxHeight:e.height}},g)}},{key:"componentDidMount",value:function(){this.props.onReset("cascader","class")}}])&&ge(t.prototype,n),i}(x),Fe=function(e){function i(e){var t;return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,i),t=r.call(this,e),et[e.el]=Le(t),t.state=t.initState(),t.bodyView=null,t}!function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&Me(e,t)}(i,e);var t,n,r=Te(i);return t=i,(n=[{key:"initState",value:function(){return{data:[],dataObj:{},flatData:[],sels:[],show:!1,tmpColor:"",bodyClass:"",time:0}}},{key:"init",value:function(e,t){var n,o=e.data,r=e.prop,i=e.initValue,a=e.radio,l=e.tree,s=e.cascader;if(t){var c={},u=[];this.load(o,c,u,null,0,i?i.map(function(e){return"object"===Ie(e)?e[r.value]:e}):null),n=this.exchangeValue(i||Object.keys(c).filter(function(e){return!0===c[e][r.selected]}),c),a&&n.length>1&&(n=n.slice(0,1),(l.show&&l.strict||s.show&&s.strict)&&this.clearAndReset(o,n,!1)),this.setState({sels:n,dataObj:c,flatData:u})}return this.setState({data:o}),n}},{key:"upDate",value:function(e,t){var n=this.state.dataObj,o=this.props,r=o.prop,i=o.tree,a=o.cascader,l=r.value,s=r.disabled,u=r.children;e.map(function(e){return n["object"===Ie(e)?e[l]:e]}).filter(function(e){return e}).forEach(function(e){if(e[s]=!t,i.show&&i.strict||a.show&&a.strict){if(t)for(var n=e;n;)n[s]=!1,n=n.__node.parent;!function e(n){n[s]=!t;var o=n[u];o&&Object(c.d)(o)&&o.forEach(function(t){return e(t)})}(e)}}),this.setState({dataObj:n})}},{key:"exchangeValue",value:function(e){var t=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.state.dataObj,o=this.props,r=o.prop,i=o.tree,a=o.cascader,l=(o.data,r.children),s=r.value,u=e.map(function(e){return"object"===Ie(e)?Re(Re({},e),{},{__node:{}}):n[e]}).filter(function(e){return e}),p=Se(u);if(i.show&&i.strict||a.show&&a.strict){var d={};d[l]=u,function e(t,n){var o=n[l];o&&Object(c.d)(o)&&o.forEach(function(n){-1===u.findIndex(function(e){return e[s]===n[s]})&&t.push(n),e(t,n)})}(p,d),p=p.filter(function(e){return!0!==e[t.props.prop.optgroup]})}return p}},{key:"value",value:function(e,t,n,o){var r=!(arguments.length>4&&void 0!==arguments[4])||arguments[4];!1!==t&&!0!==t&&(t=this.state.show);var i=this.props,a=i.prop,l=i.tree,s=i.cascader,c=this.exchangeValue(e);if(!this.checkMax(c,c,!0)){if(l.show&&l.strict||s.show&&s.strict){var u=this.state.data;this.clearAndReset(u,c,!1),c=this.init({data:u,prop:a},!0)}this.resetSelectValue(c,o||c,r,n),this.setState({show:t})}}},{key:"clearAndReset",value:function(e,t,n){var o=this,r=this.props.prop,i=r.selected,a=r.disabled,l=r.children,s=r.value;e.forEach(function(e){e[i]=-1!=t.findIndex(function(t){return t[s]===e[s]})||n;var r=e[l];if(r&&Object(c.d)(r)&&r.length>0){o.clearAndReset(r,t,e[i]);var u=r.length,p=r.filter(function(e){return!0===e[i]||!0===e.__node.selected}).length;e.__node.selected=p===u,e.__node.half=p>0&&p0,e.__node.disabled=r.filter(function(e){return!0===e[a]||!0===e.__node.disabled}).length===u}})}},{key:"load",value:function(e,t,n,o){var r=this,i=arguments.length>4&&void 0!==arguments[4]?arguments[4]:0,a=arguments.length>5?arguments[5]:void 0,l=this.props,s=l.prop,u=l.tree,p=l.cascader,f=s.children,d=s.optgroup,h=s.value,m=s.selected,b=s.disabled;e.forEach(function(e){e.__node={parent:o,level:i,loading:e.__node&&e.__node.loading},a&&(delete e[m],a.find(function(t){return t===e[h]})&&(e[m]=!0)),t[e[h]]=e,n.push(e);var l=e[f];if(l&&Object(c.d)(l)){var s=l.length;if(s>0){r.load(l,t,n,e,i+1,a),e[d]=!0,(u.show&&u.strict||p.show&&p.strict)&&(!0===e[m]&&(delete e[m],l.forEach(function(e){return e[m]=!0})),!0===e[b]&&(delete e[b],l.forEach(function(e){return e[b]=!0})));var y=l.filter(function(e){return!0===e[m]||!0===e.__node.selected}).length;e.__node.selected=y===s,e.__node.half=y>0&&y0,e.__node.disabled=l.filter(function(e){return!0===e[b]||!0===e.__node.disabled}).length===s}}})}},{key:"resetSelectValue",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],n=arguments.length>2?arguments[2]:void 0,o=!(arguments.length>3&&void 0!==arguments[3])||arguments[3],r=this.props.on;if(Object(c.e)(r)&&this.prepare&&o){var i=r({arr:e,change:t,isAdd:n});if(Object(c.d)(i))return this.value(i,null,!1)}this.setState({sels:e})}},{key:"updateBorderColor",value:function(e){this.setState({tmpColor:e})}},{key:"treeHandler",value:function(e,t,n,o,r){var i=this,a=this.props.prop,l=a.value,s=(a.selected,a.disabled),c=a.children,u=a.optgroup,p=t[c];if(p.filter(function(e){return!(e[s]||e.__node.disabled)}).forEach(function(t){if(t[u])i.treeHandler(e,t,n,o,r);else{var a=e.findIndex(function(e){return e[l]==t[l]});"del"===o?-1!=a&&(e.splice(a,1),n.push(t)):"half"!==o&&"add"!==o||-1==a&&(e.push(t),n.push(t))}}),r){var f=p.length,d=p.filter(function(t){return-1!==e.findIndex(function(e){return e[l]===t[l]})||!0===t.__node.selected}).length;t.__node.selected=d===f,t.__node.half=d>0&&dl;if(l>0&&s)return this.updateBorderColor(a.maxColor),i&&Object(c.e)(i)&&i(t,e),!0}},{key:"itemClick",value:function(e,t,n,o){var r=this.props,i=(r.theme,r.prop),a=r.radio,l=r.repeat,s=r.clickClose,c=(r.max,r.maxMethod,r.tree),u=r.cascader,p=r.data,f=Se(this.state.sels),d=i.value,h=(i.selected,i.disabled,i.children),m=i.optgroup;if(!n){if(e[m]&&(c.show&&c.strict||u.show&&u.strict)){e[h];var b,y=[],x=!0;if(e.__node.selected?(b="del",x=!1):e.__node.half?(b="half",this.treeHandler(f,e,y,b),0===y.length&&(b="del",x=!1)):b="add","half"!=b&&this.treeHandler(f,e,y,b),this.checkMax(y,f))return;f=Se(this.state.sels),y=[],this.treeHandler(f,e,y,b,!0),this.resetSelectValue(f,y,x),this.setState({data:this.state.data})}else if(!t||l&&!o){if(this.checkMax(e,f))return;f=a?[e]:[].concat(Se(f),[e]),this.clearAndReset(p,f,t),this.resetSelectValue(f,[e],!t)}else{var v=f.findIndex(function(t){return t[d]==e[d]});-1!=v&&(f.splice(v,1),this.resetSelectValue(f,[e],!t))}var g=e.__node.parent;if(g){for(;g;){var _=g[h],w=_.length,k=_.filter(function(e){return-1!==f.findIndex(function(t){return t[d]===e[d]})||!0===e.__node.selected}).length;g.__node.selected=k===w,g.__node.half=k>0&&k0,g=g.__node.parent}this.setState({data:this.state.data})}s&&!o&&this.onClick()}}},{key:"onClick",value:function(e){var t=this;if("relative"!==this.props.model.type)if(this.props.disabled)!1!==this.state.show&&this.setState({show:!1});else{var n=!this.state.show;if(n){if(this.props.show&&0==this.props.show())return;Object.keys(Xe).filter(function(e){return e!=t.props.el}).forEach(function(e){return Xe[e].closed()})}else{if(this.props.hide&&0==this.props.hide())return;this.bodyView.scroll&&this.bodyView.scroll(0,0)}this.setState({show:n}),e&&e.stopPropagation()}}},{key:"onReset",value:function(e,t){var n=this;if("data"===t){var o=e.filter(function(e){return!0===e[n.props.prop.selected]});this.resetSelectValue(Object(c.f)(o,this.state.sels,this.props.prop),o,!0);var r=[];this.load(e,{},r),this.setState({data:e,flatData:r})}else"sels"===t?this.resetSelectValue(e,e,!0):"append"===t?this.append(e):"delete"===t?this.del(e):"auto"===t?this.auto(e):"treeData"===t?this.value(e,null,!0,!1,!1):"close"===t?this.onClick():"class"===t?this.setState({bodyClass:e}):"labelSearchBlur"===t?this.labelRef.blur(e):"labelSearch"===t&&this.generalRef.labelSearch(e)}},{key:"append",value:function(e){var t=this.exchangeValue(e);this.value(Object(c.f)(t,this.state.sels,this.props.prop),this.props.show,!0,t)}},{key:"del",value:function(e){var t=this.props.prop.value,n=this.state.sels,o=this.exchangeValue(e);o.forEach(function(e){var o=n.findIndex(function(n){return n[t]===e[t]});-1!=o&&n.splice(o,1)}),this.value(n,this.props.show,!0,o,!1)}},{key:"auto",value:function(e){var t=this,n=this.props.prop.value;e.filter(function(e){return-1!=t.state.sels.findIndex(function(t){return t[n]===e[n]})}).length==e.length?this.del(e):this.append(e)}},{key:"changeExpandedKeys",value:function(e){var t=this.props,n=t.tree,o=t.prop,r=this.state,i=r.dataObj,a=r.flatData;n.show&&this.treeRef.init({dataObj:i,flatData:a,prop:o,tree:{expandedKeys:e}})}},{key:"calcPosition",value:function(){if(this.state.show&&"fixed"===this.props.model.type){var e=this.base.getBoundingClientRect();return Date.now()-this.state.time>10&&this.setState({time:Date.now()}),{position:"fixed",left:e.x,top:e.y+e.height+4,width:e.width}}return{}}},{key:"componentWillReceiveProps",value:function(e){this.init(e,e.updateData)}},{key:"componentWillMount",value:function(){this.init(this.props,!0)}},{key:"render",value:function(e,t){var n=this,o=e.theme,r=e.prop,i=(e.radio,e.repeat,e.clickClose,e.on,e.max,e.maxMethod,e.content),a=e.disabled,l=e.tree,s=e.submitConversion,u={borderColor:o.color},p=t.data,f=t.dataObj,d=t.flatData,h=t.sels,b=t.show,y=t.tmpColor,x=t.bodyClass;a&&(b=!1);var v={style:Re(Re({},e.style),b?u:{}),onClick:this.onClick.bind(this),ua:Object(c.a)(),size:e.size,tabindex:1};y&&(v.style.borderColor=y,setTimeout(function(){v.style.borderColor="",n.updateBorderColor("")},300)),r.value;var g=Re(Re({},e),{},{data:p,sels:h,ck:this.itemClick.bind(this),title:h.map(function(e){return e[r.name]}).join(","),onReset:this.onReset.bind(this)}),_=Re(Re({},e),{},{data:p,dataObj:f,flatData:d,sels:h,ck:this.itemClick.bind(this),show:b,onReset:this.onReset.bind(this)}),w=i?m(ue,_):l.show?m(xe,je({},_,{ref:function(e){return n.treeRef=e}})):e.cascader.show?m(Ce,_):m(oe,je({},_,{ref:function(e){return n.generalRef=e}})),k=this.calcPosition();return m("xm-select",v,m("input",{class:"xm-select-default","lay-verify":e.layVerify,"lay-verType":e.layVerType,"lay-reqText":e.layReqText,name:e.name,value:s(h,r)}),m("i",{class:b?"xm-icon xm-icon-expand":"xm-icon"}),0===h.length&&m("div",{class:"xm-tips"},e.tips),m(H,je({},g,{ref:function(e){ @@ -46,9 +46,9 @@ self.elem.results.children("li").length?(self.prop.key_select=!0,self.nextLine(s },REGEXP_DATA_URL_HEAD=/^data:.*,/,REGEXP_USERAGENT=/(Macintosh|iPhone|iPod|iPad).*AppleWebKit/i,navigator="undefined"!=typeof window?window.navigator:null,IS_SAFARI_OR_UIWEBVIEW=navigator&®EXP_USERAGENT.test(navigator.userAgent),fromCharCode=String.fromCharCode,render={render:function(){var self=this;self.initContainer(),self.initCanvas(),self.initCropBox(),self.renderCanvas(),self.cropped&&self.renderCropBox()},initContainer:function(){var self=this,options=self.options,$this=self.$element,$container=self.$container,$cropper=self.$cropper,hidden="cropper-hidden";$cropper.addClass(hidden),$this.removeClass(hidden),$cropper.css(self.container={width:Math.max($container.width(),Number(options.minContainerWidth)||200),height:Math.max($container.height(),Number(options.minContainerHeight)||100)}),$this.addClass(hidden),$cropper.removeClass(hidden)},initCanvas:function(){var self=this,viewMode=self.options.viewMode,container=self.container,containerWidth=container.width,containerHeight=container.height,image=self.image,imageNaturalWidth=image.naturalWidth,imageNaturalHeight=image.naturalHeight,is90Degree=Math.abs(image.rotate)%180==90,naturalWidth=is90Degree?imageNaturalHeight:imageNaturalWidth,naturalHeight=is90Degree?imageNaturalWidth:imageNaturalHeight,aspectRatio=naturalWidth/naturalHeight,canvasWidth=containerWidth,canvasHeight=containerHeight;containerHeight*aspectRatio>containerWidth?3===viewMode?canvasWidth=containerHeight*aspectRatio:canvasHeight=containerWidth/aspectRatio:3===viewMode?canvasHeight=containerWidth/aspectRatio:canvasWidth=containerHeight*aspectRatio;var canvas={naturalWidth:naturalWidth,naturalHeight:naturalHeight,aspectRatio:aspectRatio,width:canvasWidth,height:canvasHeight};canvas.left=(containerWidth-canvasWidth)/2,canvas.top=(containerHeight-canvasHeight)/2,canvas.oldLeft=canvas.left,canvas.oldTop=canvas.top,self.canvas=canvas,self.limited=1===viewMode||2===viewMode,self.limitCanvas(!0,!0),self.initialImage=$.extend({},image),self.initialCanvas=$.extend({},canvas)},limitCanvas:function(isSizeLimited,isPositionLimited){var self=this,options=self.options,viewMode=options.viewMode,container=self.container,containerWidth=container.width,containerHeight=container.height,canvas=self.canvas,aspectRatio=canvas.aspectRatio,cropBox=self.cropBox,cropped=self.cropped&&cropBox;if(isSizeLimited){var minCanvasWidth=Number(options.minCanvasWidth)||0,minCanvasHeight=Number(options.minCanvasHeight)||0;viewMode&&(viewMode>1?(minCanvasWidth=Math.max(minCanvasWidth,containerWidth),minCanvasHeight=Math.max(minCanvasHeight,containerHeight),3===viewMode&&(minCanvasHeight*aspectRatio>minCanvasWidth?minCanvasWidth=minCanvasHeight*aspectRatio:minCanvasHeight=minCanvasWidth/aspectRatio)):minCanvasWidth?minCanvasWidth=Math.max(minCanvasWidth,cropped?cropBox.width:0):minCanvasHeight?minCanvasHeight=Math.max(minCanvasHeight,cropped?cropBox.height:0):cropped&&(minCanvasWidth=cropBox.width,minCanvasHeight=cropBox.height,minCanvasHeight*aspectRatio>minCanvasWidth?minCanvasWidth=minCanvasHeight*aspectRatio:minCanvasHeight=minCanvasWidth/aspectRatio)),minCanvasWidth&&minCanvasHeight?minCanvasHeight*aspectRatio>minCanvasWidth?minCanvasHeight=minCanvasWidth/aspectRatio:minCanvasWidth=minCanvasHeight*aspectRatio:minCanvasWidth?minCanvasHeight=minCanvasWidth/aspectRatio:minCanvasHeight&&(minCanvasWidth=minCanvasHeight*aspectRatio),canvas.minWidth=minCanvasWidth,canvas.minHeight=minCanvasHeight,canvas.maxWidth=1/0,canvas.maxHeight=1/0}if(isPositionLimited)if(viewMode){var newCanvasLeft=containerWidth-canvas.width,newCanvasTop=containerHeight-canvas.height;canvas.minLeft=Math.min(0,newCanvasLeft),canvas.minTop=Math.min(0,newCanvasTop),canvas.maxLeft=Math.max(0,newCanvasLeft),canvas.maxTop=Math.max(0,newCanvasTop),cropped&&self.limited&&(canvas.minLeft=Math.min(cropBox.left,cropBox.left+cropBox.width-canvas.width),canvas.minTop=Math.min(cropBox.top,cropBox.top+cropBox.height-canvas.height),canvas.maxLeft=cropBox.left,canvas.maxTop=cropBox.top,2===viewMode&&(canvas.width>=containerWidth&&(canvas.minLeft=Math.min(0,newCanvasLeft),canvas.maxLeft=Math.max(0,newCanvasLeft)),canvas.height>=containerHeight&&(canvas.minTop=Math.min(0,newCanvasTop),canvas.maxTop=Math.max(0,newCanvasTop))))}else canvas.minLeft=-canvas.width,canvas.minTop=-canvas.height,canvas.maxLeft=containerWidth,canvas.maxTop=containerHeight},renderCanvas:function(isChanged){var self=this,canvas=self.canvas,image=self.image,rotate=image.rotate,naturalWidth=image.naturalWidth,naturalHeight=image.naturalHeight;if(self.rotated){self.rotated=!1;var rotated=getRotatedSizes({width:image.width,height:image.height,degree:rotate}),aspectRatio=rotated.width/rotated.height,isSquareImage=1===image.aspectRatio;if(isSquareImage||aspectRatio!==canvas.aspectRatio){if(canvas.left-=(rotated.width-canvas.width)/2,canvas.top-=(rotated.height-canvas.height)/2,canvas.width=rotated.width,canvas.height=rotated.height,canvas.aspectRatio=aspectRatio,canvas.naturalWidth=naturalWidth,canvas.naturalHeight=naturalHeight,isSquareImage&&rotate%90||rotate%180){var rotated2=getRotatedSizes({width:naturalWidth,height:naturalHeight,degree:rotate});canvas.naturalWidth=rotated2.width,canvas.naturalHeight=rotated2.height}self.limitCanvas(!0,!1)}}(canvas.width>canvas.maxWidth||canvas.widthcanvas.maxHeight||canvas.heightcanvas.width?cropBox.height=cropBox.width/aspectRatio:cropBox.width=cropBox.height*aspectRatio),self.cropBox=cropBox,self.limitCropBox(!0,!0),cropBox.width=Math.min(Math.max(cropBox.width,cropBox.minWidth),cropBox.maxWidth),cropBox.height=Math.min(Math.max(cropBox.height,cropBox.minHeight),cropBox.maxHeight),cropBox.width=Math.max(cropBox.minWidth,cropBox.width*autoCropArea),cropBox.height=Math.max(cropBox.minHeight,cropBox.height*autoCropArea),cropBox.left=canvas.left+(canvas.width-cropBox.width)/2,cropBox.top=canvas.top+(canvas.height-cropBox.height)/2,cropBox.oldLeft=cropBox.left,cropBox.oldTop=cropBox.top,self.initialCropBox=$.extend({},cropBox)},limitCropBox:function(isSizeLimited,isPositionLimited){var self=this,options=self.options,aspectRatio=options.aspectRatio,container=self.container,containerWidth=container.width,containerHeight=container.height,canvas=self.canvas,cropBox=self.cropBox,limited=self.limited;if(isSizeLimited){var minCropBoxWidth=Number(options.minCropBoxWidth)||0,minCropBoxHeight=Number(options.minCropBoxHeight)||0,maxCropBoxWidth=Math.min(containerWidth,limited?canvas.width:containerWidth),maxCropBoxHeight=Math.min(containerHeight,limited?canvas.height:containerHeight);minCropBoxWidth=Math.min(minCropBoxWidth,containerWidth),minCropBoxHeight=Math.min(minCropBoxHeight,containerHeight),aspectRatio&&(minCropBoxWidth&&minCropBoxHeight?minCropBoxHeight*aspectRatio>minCropBoxWidth?minCropBoxHeight=minCropBoxWidth/aspectRatio:minCropBoxWidth=minCropBoxHeight*aspectRatio:minCropBoxWidth?minCropBoxHeight=minCropBoxWidth/aspectRatio:minCropBoxHeight&&(minCropBoxWidth=minCropBoxHeight*aspectRatio),maxCropBoxHeight*aspectRatio>maxCropBoxWidth?maxCropBoxHeight=maxCropBoxWidth/aspectRatio:maxCropBoxWidth=maxCropBoxHeight*aspectRatio),cropBox.minWidth=Math.min(minCropBoxWidth,maxCropBoxWidth),cropBox.minHeight=Math.min(minCropBoxHeight,maxCropBoxHeight),cropBox.maxWidth=maxCropBoxWidth,cropBox.maxHeight=maxCropBoxHeight}isPositionLimited&&(limited?(cropBox.minLeft=Math.max(0,canvas.left),cropBox.minTop=Math.max(0,canvas.top),cropBox.maxLeft=Math.min(containerWidth,canvas.left+canvas.width)-cropBox.width,cropBox.maxTop=Math.min(containerHeight,canvas.top+canvas.height)-cropBox.height):(cropBox.minLeft=0,cropBox.minTop=0,cropBox.maxLeft=containerWidth-cropBox.width,cropBox.maxTop=containerHeight-cropBox.height))},renderCropBox:function(){var self=this,options=self.options,container=self.container,containerWidth=container.width,containerHeight=container.height,cropBox=self.cropBox;(cropBox.width>cropBox.maxWidth||cropBox.widthcropBox.maxHeight||cropBox.heightoriginalHeight&&(ratio=originalHeight/cropBoxHeight,newWidth=cropBoxWidth*ratio,newHeight=originalHeight),$this.css({width:newWidth,height:newHeight}).find("img").css({width:width*ratio,height:height*ratio,transform:getTransform($.extend({translateX:-left*ratio,translateY:-top*ratio},image))})}))}},PointerEvent="undefined"!=typeof window?window.PointerEvent:null,EVENT_POINTER_DOWN=PointerEvent?"pointerdown":"touchstart mousedown",EVENT_POINTER_MOVE=PointerEvent?"pointermove":"touchmove mousemove",EVENT_POINTER_UP=PointerEvent?" pointerup pointercancel":"touchend touchcancel mouseup",events={bind:function(){var self=this,options=self.options,$this=self.$element,$cropper=self.$cropper;$.isFunction(options.cropstart)&&$this.on("cropstart",options.cropstart),$.isFunction(options.cropmove)&&$this.on("cropmove",options.cropmove),$.isFunction(options.cropend)&&$this.on("cropend",options.cropend),$.isFunction(options.crop)&&$this.on("crop",options.crop),$.isFunction(options.zoom)&&$this.on("zoom",options.zoom),$cropper.on(EVENT_POINTER_DOWN,proxy(self.cropStart,this)),options.zoomable&&options.zoomOnWheel&&$cropper.on("wheel mousewheel DOMMouseScroll",proxy(self.wheel,this)),options.toggleDragModeOnDblclick&&$cropper.on("dblclick",proxy(self.dblclick,this)),$(document).on(EVENT_POINTER_MOVE,self.onCropMove=proxy(self.cropMove,this)).on(EVENT_POINTER_UP,self.onCropEnd=proxy(self.cropEnd,this)),options.responsive&&$(window).on("resize",self.onResize=proxy(self.resize,this))},unbind:function(){var self=this,options=self.options,$this=self.$element,$cropper=self.$cropper;$.isFunction(options.cropstart)&&$this.off("cropstart",options.cropstart),$.isFunction(options.cropmove)&&$this.off("cropmove",options.cropmove),$.isFunction(options.cropend)&&$this.off("cropend",options.cropend),$.isFunction(options.crop)&&$this.off("crop",options.crop),$.isFunction(options.zoom)&&$this.off("zoom",options.zoom),$cropper.off(EVENT_POINTER_DOWN,self.cropStart),options.zoomable&&options.zoomOnWheel&&$cropper.off("wheel mousewheel DOMMouseScroll",self.wheel),options.toggleDragModeOnDblclick&&$cropper.off("dblclick",self.dblclick),$(document).off(EVENT_POINTER_MOVE,self.onCropMove).off(EVENT_POINTER_UP,self.onCropEnd),options.responsive&&$(window).off("resize",self.onResize)}},REGEXP_ACTIONS=/^(e|w|s|n|se|sw|ne|nw|all|crop|move|zoom)$/,handlers={resize:function(){var self=this,options=self.options,$container=self.$container,container=self.container,minContainerWidth=Number(options.minContainerWidth)||200,minContainerHeight=Number(options.minContainerHeight)||100;if(!self.disabled&&container.width!==minContainerWidth&&container.height!==minContainerHeight){var ratio=$container.width()/container.width;if(1!==ratio||$container.height()!==container.height){var canvasData=void 0,cropBoxData=void 0;options.restore&&(canvasData=self.getCanvasData(),cropBoxData=self.getCropBoxData()),self.render(),options.restore&&(self.setCanvasData($.each(canvasData,function(i,n){canvasData[i]=n*ratio})),self.setCropBoxData($.each(cropBoxData,function(i,n){cropBoxData[i]=n*ratio})))}}},dblclick:function(){var self=this;self.disabled||"none"===self.options.dragMode||self.setDragMode(self.$dragBox.hasClass("cropper-crop")?"move":"crop")},wheel:function(event){var self=this,e=event.originalEvent||event,ratio=Number(self.options.wheelZoomRatio)||.1;if(!self.disabled&&(event.preventDefault(),!self.wheeling)){self.wheeling=!0,setTimeout(function(){self.wheeling=!1},50);var delta=1;e.deltaY?delta=e.deltaY>0?1:-1:e.wheelDelta?delta=-e.wheelDelta/120:e.detail&&(delta=e.detail>0?1:-1),self.zoom(-delta*ratio,event)}},cropStart:function(e){var self=this;if(!self.disabled){var options=self.options,pointers=self.pointers,originalEvent=e.originalEvent,action=void 0;originalEvent&&originalEvent.changedTouches?$.each(originalEvent.changedTouches,function(i,touch){pointers[touch.identifier]=getPointer(touch)}):pointers[originalEvent&&originalEvent.pointerId||0]=getPointer(originalEvent||e),action=objectKeys(pointers).length>1&&options.zoomable&&options.zoomOnTouch?"zoom":$(e.target).data("action"),REGEXP_ACTIONS.test(action)&&(self.trigger("cropstart",{originalEvent:originalEvent,action:action}).isDefaultPrevented()||(e.preventDefault(),self.action=action,self.cropping=!1,"crop"===action&&(self.cropping=!0,self.$dragBox.addClass("cropper-modal"))))}},cropMove:function(e){var self=this,action=self.action;if(!self.disabled&&action){var pointers=self.pointers,originalEvent=e.originalEvent;e.preventDefault(),self.trigger("cropmove",{originalEvent:originalEvent,action:action}).isDefaultPrevented()||(originalEvent&&originalEvent.changedTouches?$.each(originalEvent.changedTouches,function(i,touch){$.extend(pointers[touch.identifier],getPointer(touch,!0))}):$.extend(pointers[originalEvent&&originalEvent.pointerId||0],getPointer(originalEvent||e,!0)),self.change(e))}},cropEnd:function(e){var self=this;if(!self.disabled){var action=self.action,pointers=self.pointers,originalEvent=e.originalEvent;originalEvent&&originalEvent.changedTouches?$.each(originalEvent.changedTouches,function(i,touch){delete pointers[touch.identifier]}):delete pointers[originalEvent&&originalEvent.pointerId||0],action&&(e.preventDefault(),objectKeys(pointers).length||(self.action=""),self.cropping&&(self.cropping=!1,self.$dragBox.toggleClass("cropper-modal",self.cropped&&self.options.modal)),self.trigger("cropend",{originalEvent:originalEvent,action:action}))}}},change={change:function(e){var self=this,options=self.options,pointers=self.pointers,pointer=pointers[objectKeys(pointers)[0]],container=self.container,canvas=self.canvas,cropBox=self.cropBox,action=self.action,aspectRatio=options.aspectRatio,width=cropBox.width,height=cropBox.height,left=cropBox.left,top=cropBox.top,right=left+width,bottom=top+height,minLeft=0,minTop=0,maxWidth=container.width,maxHeight=container.height,renderable=!0,offset=void 0;!aspectRatio&&e.shiftKey&&(aspectRatio=width&&height?width/height:1),self.limited&&(minLeft=cropBox.minLeft,minTop=cropBox.minTop,maxWidth=minLeft+Math.min(container.width,canvas.width,canvas.left+canvas.width),maxHeight=minTop+Math.min(container.height,canvas.height,canvas.top+canvas.height));var range={x:pointer.endX-pointer.startX,y:pointer.endY-pointer.startY};switch(action){case"all":left+=range.x,top+=range.y;break;case"e":if(range.x>=0&&(right>=maxWidth||aspectRatio&&(top<=minTop||bottom>=maxHeight))){renderable=!1;break}right+range.x>maxWidth&&(range.x=maxWidth-right),width+=range.x,aspectRatio&&(height=width/aspectRatio,top-=range.x/aspectRatio/2),width<0&&(action="w",width=0);break;case"n":if(range.y<=0&&(top<=minTop||aspectRatio&&(left<=minLeft||right>=maxWidth))){renderable=!1;break}top+range.y=maxHeight))){renderable=!1;break}left+range.x=0&&(bottom>=maxHeight||aspectRatio&&(left<=minLeft||right>=maxWidth))){renderable=!1;break}bottom+range.y>maxHeight&&(range.y=maxHeight-bottom),height+=range.y,aspectRatio&&(width=height*aspectRatio,left-=range.y*aspectRatio/2),height<0&&(action="n",height=0);break;case"ne":if(aspectRatio){if(range.y<=0&&(top<=minTop||right>=maxWidth)){renderable=!1;break}height-=range.y,top+=range.y,width=height*aspectRatio}else range.x>=0?rightminTop&&(height-=range.y,top+=range.y):(height-=range.y,top+=range.y);width<0&&height<0?(action="sw",height=0,width=0):width<0?(action="nw",width=0):height<0&&(action="se",height=0);break;case"nw":if(aspectRatio){if(range.y<=0&&(top<=minTop||left<=minLeft)){renderable=!1;break}height-=range.y,top+=range.y,width=height*aspectRatio,left+=range.y*aspectRatio}else range.x<=0?left>minLeft?(width-=range.x,left+=range.x):range.y<=0&&top<=minTop&&(renderable=!1):(width-=range.x,left+=range.x),range.y<=0?top>minTop&&(height-=range.y,top+=range.y):(height-=range.y,top+=range.y);width<0&&height<0?(action="se",height=0,width=0):width<0?(action="ne",width=0):height<0&&(action="sw",height=0);break;case"sw":if(aspectRatio){if(range.x<=0&&(left<=minLeft||bottom>=maxHeight)){renderable=!1;break}width-=range.x,left+=range.x,height=width/aspectRatio}else range.x<=0?left>minLeft?(width-=range.x,left+=range.x):range.y>=0&&bottom>=maxHeight&&(renderable=!1):(width-=range.x,left+=range.x),range.y>=0?bottom=0&&(right>=maxWidth||bottom>=maxHeight)){renderable=!1;break}width+=range.x,height=width/aspectRatio}else range.x>=0?right=0&&bottom>=maxHeight&&(renderable=!1):width+=range.x,range.y>=0?bottom0?action=range.y>0?"se":"ne":range.x<0&&(left-=width,action=range.y>0?"sw":"nw"),range.y<0&&(top-=height),self.cropped||(self.$cropBox.removeClass("cropper-hidden"),self.cropped=!0,self.limited&&self.limitCropBox(!0,!0))}renderable&&(cropBox.width=width,cropBox.height=height,cropBox.left=left,cropBox.top=top,self.action=action,self.renderCropBox()),$.each(pointers,function(i,p){p.startX=p.endX,p.startY=p.endY})}},methods={crop:function(){var self=this;self.ready&&!self.disabled&&(self.cropped||(self.cropped=!0,self.limitCropBox(!0,!0),self.options.modal&&self.$dragBox.addClass("cropper-modal"),self.$cropBox.removeClass("cropper-hidden")),self.setCropBoxData(self.initialCropBox))},reset:function(){var self=this;self.ready&&!self.disabled&&(self.image=$.extend({},self.initialImage),self.canvas=$.extend({},self.initialCanvas),self.cropBox=$.extend({},self.initialCropBox),self.renderCanvas(),self.cropped&&self.renderCropBox())},clear:function(){var self=this;self.cropped&&!self.disabled&&($.extend(self.cropBox,{left:0,top:0,width:0,height:0}),self.cropped=!1,self.renderCropBox(),self.limitCanvas(!0,!0),self.renderCanvas(),self.$dragBox.removeClass("cropper-modal"),self.$cropBox.addClass("cropper-hidden"))},replace:function(url,onlyColorChanged){var self=this;!self.disabled&&url&&(self.isImg&&self.$element.attr("src",url),onlyColorChanged?(self.url=url,self.$clone.attr("src",url),self.ready&&self.$preview.find("img").add(self.$clone2).attr("src",url)):(self.isImg&&(self.replaced=!0),self.options.data=null,self.load(url)))},enable:function(){var self=this;self.ready&&(self.disabled=!1,self.$cropper.removeClass("cropper-disabled"))},disable:function(){var self=this;self.ready&&(self.disabled=!0,self.$cropper.addClass("cropper-disabled"))},destroy:function(){var self=this,$this=self.$element;self.loaded?(self.isImg&&self.replaced&&$this.attr("src",self.originalUrl),self.unbuild(),$this.removeClass("cropper-hidden")):self.isImg?$this.off("load",self.start):self.$clone&&self.$clone.remove(),$this.removeData("cropper")},move:function(offsetX,offsetY){var self=this,canvas=self.canvas;self.moveTo(isUndefined(offsetX)?offsetX:canvas.left+Number(offsetX),isUndefined(offsetY)?offsetY:canvas.top+Number(offsetY))},moveTo:function(x,y){var self=this,canvas=self.canvas,changed=!1;isUndefined(y)&&(y=x),x=Number(x),y=Number(y),self.ready&&!self.disabled&&self.options.movable&&(isNumber(x)&&(canvas.left=x,changed=!0),isNumber(y)&&(canvas.top=y,changed=!0),changed&&self.renderCanvas(!0))},zoom:function(ratio,_event){var self=this,canvas=self.canvas;ratio=Number(ratio),ratio=ratio<0?1/(1-ratio):1+ratio,self.zoomTo(canvas.width*ratio/canvas.naturalWidth,_event)},zoomTo:function(ratio,_event){var self=this,options=self.options,pointers=self.pointers,canvas=self.canvas,width=canvas.width,height=canvas.height,naturalWidth=canvas.naturalWidth,naturalHeight=canvas.naturalHeight;if((ratio=Number(ratio))>=0&&self.ready&&!self.disabled&&options.zoomable){var newWidth=naturalWidth*ratio,newHeight=naturalHeight*ratio,originalEvent=void 0;if(_event&&(originalEvent=_event.originalEvent),self.trigger("zoom",{originalEvent:originalEvent,oldRatio:width/naturalWidth,ratio:newWidth/naturalWidth}).isDefaultPrevented())return;if(originalEvent){var offset=self.$cropper.offset(),center=pointers&&objectKeys(pointers).length?getPointersCenter(pointers):{pageX:_event.pageX||originalEvent.pageX||0,pageY:_event.pageY||originalEvent.pageY||0};canvas.left-=(newWidth-width)*((center.pageX-offset.left-canvas.left)/width),canvas.top-=(newHeight-height)*((center.pageY-offset.top-canvas.top)/height)}else canvas.left-=(newWidth-width)/2,canvas.top-=(newHeight-height)/2;canvas.width=newWidth,canvas.height=newHeight,self.renderCanvas(!0)}},rotate:function(degree){var self=this;self.rotateTo((self.image.rotate||0)+Number(degree))},rotateTo:function(degree){var self=this;degree=Number(degree),isNumber(degree)&&self.ready&&!self.disabled&&self.options.rotatable&&(self.image.rotate=degree%360,self.rotated=!0,self.renderCanvas(!0))},scale:function(scaleX,scaleY){var self=this,image=self.image,changed=!1;isUndefined(scaleY)&&(scaleY=scaleX),scaleX=Number(scaleX),scaleY=Number(scaleY),self.ready&&!self.disabled&&self.options.scalable&&(isNumber(scaleX)&&(image.scaleX=scaleX,changed=!0),isNumber(scaleY)&&(image.scaleY=scaleY,changed=!0),changed&&self.renderImage(!0))},scaleX:function(_scaleX){var self=this,scaleY=self.image.scaleY;self.scale(_scaleX,isNumber(scaleY)?scaleY:1)},scaleY:function(_scaleY){var self=this,scaleX=self.image.scaleX;self.scale(isNumber(scaleX)?scaleX:1,_scaleY)},getData:function(isRounded){var self=this,options=self.options,image=self.image,canvas=self.canvas,cropBox=self.cropBox,ratio=void 0,data=void 0;return self.ready&&self.cropped?(data={x:cropBox.left-canvas.left,y:cropBox.top-canvas.top,width:cropBox.width,height:cropBox.height},ratio=image.width/image.naturalWidth,$.each(data,function(i,n){n/=ratio,data[i]=isRounded?Math.round(n):n})):data={x:0,y:0,width:0,height:0},options.rotatable&&(data.rotate=image.rotate||0),options.scalable&&(data.scaleX=image.scaleX||1,data.scaleY=image.scaleY||1),data},setData:function(data){var self=this,options=self.options,image=self.image,canvas=self.canvas,cropBoxData={},rotated=void 0,isScaled=void 0,ratio=void 0;$.isFunction(data)&&(data=data.call(self.element)),self.ready&&!self.disabled&&$.isPlainObject(data)&&(options.rotatable&&isNumber(data.rotate)&&data.rotate!==image.rotate&&(image.rotate=data.rotate,rotated=!0,self.rotated=rotated),options.scalable&&(isNumber(data.scaleX)&&data.scaleX!==image.scaleX&&(image.scaleX=data.scaleX,isScaled=!0),isNumber(data.scaleY)&&data.scaleY!==image.scaleY&&(image.scaleY=data.scaleY,isScaled=!0)),rotated?self.renderCanvas():isScaled&&self.renderImage(),ratio=image.width/image.naturalWidth,isNumber(data.x)&&(cropBoxData.left=data.x*ratio+canvas.left),isNumber(data.y)&&(cropBoxData.top=data.y*ratio+canvas.top),isNumber(data.width)&&(cropBoxData.width=data.width*ratio),isNumber(data.height)&&(cropBoxData.height=data.height*ratio),self.setCropBoxData(cropBoxData))},getContainerData:function(){return this.ready?this.container:{}},getImageData:function(){return this.loaded?this.image:{}},getCanvasData:function(){var self=this,canvas=self.canvas,data={};return self.ready&&$.each(["left","top","width","height","naturalWidth","naturalHeight"],function(i,n){data[n]=canvas[n]}),data},setCanvasData:function(data){var self=this,canvas=self.canvas,aspectRatio=canvas.aspectRatio;$.isFunction(data)&&(data=data.call(self.$element)),self.ready&&!self.disabled&&$.isPlainObject(data)&&(isNumber(data.left)&&(canvas.left=data.left),isNumber(data.top)&&(canvas.top=data.top),isNumber(data.width)?(canvas.width=data.width,canvas.height=data.width/aspectRatio):isNumber(data.height)&&(canvas.height=data.height,canvas.width=data.height*aspectRatio),self.renderCanvas(!0))},getCropBoxData:function(){var self=this,cropBox=self.cropBox;return self.ready&&self.cropped?{left:cropBox.left,top:cropBox.top,width:cropBox.width,height:cropBox.height}:{}},setCropBoxData:function(data){var self=this,cropBox=self.cropBox,aspectRatio=self.options.aspectRatio,widthChanged=void 0,heightChanged=void 0;$.isFunction(data)&&(data=data.call(self.$element)),self.ready&&self.cropped&&!self.disabled&&$.isPlainObject(data)&&(isNumber(data.left)&&(cropBox.left=data.left),isNumber(data.top)&&(cropBox.top=data.top),isNumber(data.width)&&data.width!==cropBox.width&&(widthChanged=!0,cropBox.width=data.width),isNumber(data.height)&&data.height!==cropBox.height&&(heightChanged=!0,cropBox.height=data.height),aspectRatio&&(widthChanged?cropBox.height=cropBox.width/aspectRatio:heightChanged&&(cropBox.width=cropBox.height*aspectRatio)),self.renderCropBox())},getCroppedCanvas:function(options){var self=this;if(!self.ready||!window.HTMLCanvasElement)return null;if($.isPlainObject(options)||(options={}),!self.cropped)return getSourceCanvas(self.$clone[0],self.image,options);var data=self.getData(),originalWidth=data.width,originalHeight=data.height,aspectRatio=originalWidth/originalHeight,scaledWidth=void 0,scaledHeight=void 0,scaledRatio=void 0;$.isPlainObject(options)&&(scaledWidth=options.width,scaledHeight=options.height,scaledWidth?(scaledHeight=scaledWidth/aspectRatio,scaledRatio=scaledWidth/originalWidth):scaledHeight&&(scaledWidth=scaledHeight*aspectRatio,scaledRatio=scaledHeight/originalHeight));var canvasWidth=Math.floor(scaledWidth||originalWidth),canvasHeight=Math.floor(scaledHeight||originalHeight),canvas=$("")[0],context=canvas.getContext("2d");canvas.width=canvasWidth,canvas.height=canvasHeight,options.fillColor&&(context.fillStyle=options.fillColor,context.fillRect(0,0,canvasWidth,canvasHeight));var parameters=function(){var source=getSourceCanvas(self.$clone[0],self.image,options),sourceWidth=source.width,sourceHeight=source.height,canvasData=self.canvas,params=[source],srcX=data.x+canvasData.naturalWidth*(Math.abs(data.scaleX||1)-1)/2,srcY=data.y+canvasData.naturalHeight*(Math.abs(data.scaleY||1)-1)/2,srcWidth=void 0,srcHeight=void 0,dstX=void 0,dstY=void 0,dstWidth=void 0,dstHeight=void 0;return srcX<=-originalWidth||srcX>sourceWidth?(srcX=0,srcWidth=0,dstX=0,dstWidth=0):srcX<=0?(dstX=-srcX,srcX=0,dstWidth=Math.min(sourceWidth,originalWidth+srcX),srcWidth=dstWidth):srcX<=sourceWidth&&(dstX=0,dstWidth=Math.min(originalWidth,sourceWidth-srcX),srcWidth=dstWidth),srcWidth<=0||srcY<=-originalHeight||srcY>sourceHeight?(srcY=0,srcHeight=0,dstY=0,dstHeight=0):srcY<=0?(dstY=-srcY,srcY=0,dstHeight=Math.min(sourceHeight,originalHeight+srcY),srcHeight=dstHeight):srcY<=sourceHeight&&(dstY=0,dstHeight=Math.min(originalHeight,sourceHeight-srcY),srcHeight=dstHeight),params.push(Math.floor(srcX),Math.floor(srcY),Math.floor(srcWidth),Math.floor(srcHeight)),scaledRatio&&(dstX*=scaledRatio,dstY*=scaledRatio,dstWidth*=scaledRatio,dstHeight*=scaledRatio),dstWidth>0&&dstHeight>0&¶ms.push(Math.floor(dstX),Math.floor(dstY),Math.floor(dstWidth),Math.floor(dstHeight)),params}();return context.imageSmoothingEnabled=!!options.imageSmoothingEnabled, options.imageSmoothingQuality&&(context.imageSmoothingQuality=options.imageSmoothingQuality),context.drawImage.apply(context,_toConsumableArray(parameters)),canvas},setAspectRatio:function(aspectRatio){var self=this,options=self.options;self.disabled||isUndefined(aspectRatio)||(options.aspectRatio=Math.max(0,aspectRatio)||NaN,self.ready&&(self.initCropBox(),self.cropped&&self.renderCropBox()))},setDragMode:function(mode){var self=this,options=self.options,croppable=void 0,movable=void 0;self.loaded&&!self.disabled&&(croppable="crop"===mode,movable=options.movable&&"move"===mode,mode=croppable||movable?mode:"none",self.$dragBox.data("action",mode).toggleClass("cropper-crop",croppable).toggleClass("cropper-move",movable),options.cropBoxMovable||self.$face.data("action",mode).toggleClass("cropper-crop",croppable).toggleClass("cropper-move",movable))}},_createClass=function(){function defineProperties(target,props){for(var i=0;i1)switch(self.url=arrayBufferToDataURL(arrayBuffer),orientation){case 2:scaleX=-1;break;case 3:rotate=-180;break;case 4:scaleY=-1;break;case 5:rotate=90,scaleY=-1;break;case 6:rotate=90;break;case 7:rotate=90,scaleX=-1;break;case 8:rotate=-90}options.rotatable&&(image.rotate=rotate),options.scalable&&(image.scaleX=scaleX,image.scaleY=scaleY),self.clone()}},{key:"clone",value:function(){var self=this,options=self.options,$this=self.$element,url=self.url,crossOrigin="",crossOriginUrl=void 0;options.checkCrossOrigin&&isCrossOriginURL(url)&&(crossOrigin=$this.prop("crossOrigin"),crossOrigin?crossOriginUrl=url:(crossOrigin="anonymous",crossOriginUrl=addTimestamp(url))),self.crossOrigin=crossOrigin,self.crossOriginUrl=crossOriginUrl;var image=document.createElement("img");crossOrigin&&(image.crossOrigin=crossOrigin),image.src=crossOriginUrl||url;var $clone=$(image);self.$clone=$clone,self.isImg?$this[0].complete?self.start():$this.one("load",$.proxy(self.start,this)):$clone.one("load",$.proxy(self.start,this)).one("error",$.proxy(self.stop,this)).addClass("cropper-hide").insertAfter($this)}},{key:"start",value:function(){var self=this,$clone=self.$clone,$image=self.$element;self.isImg||($clone.off("error",self.stop),$image=$clone),getImageSize($image[0],function(naturalWidth,naturalHeight){$.extend(self.image,{naturalWidth:naturalWidth,naturalHeight:naturalHeight,aspectRatio:naturalWidth/naturalHeight}),self.loaded=!0,self.build()})}},{key:"stop",value:function(){var self=this;self.$clone.remove(),self.$clone=null}},{key:"build",value:function(){var self=this,options=self.options,$this=self.$element,$clone=self.$clone;if(self.loaded){self.ready&&self.unbuild();var $cropper=$('
                '),$cropBox=$cropper.find(".cropper-crop-box"),$face=$cropBox.find(".cropper-face");self.$container=$this.parent(),self.$cropper=$cropper,self.$canvas=$cropper.find(".cropper-canvas").append($clone),self.$dragBox=$cropper.find(".cropper-drag-box"),self.$cropBox=$cropBox,self.$viewBox=$cropper.find(".cropper-view-box"),self.$face=$face,$this.addClass("cropper-hidden").after($cropper),self.isImg||$clone.removeClass("cropper-hide"),self.initPreview(),self.bind(),options.aspectRatio=Math.max(0,options.aspectRatio)||NaN,options.viewMode=Math.max(0,Math.min(3,Math.round(options.viewMode)))||0,self.cropped=options.autoCrop,options.autoCrop?options.modal&&self.$dragBox.addClass("cropper-modal"):$cropBox.addClass("cropper-hidden"),options.guides||$cropBox.find(".cropper-dashed").addClass("cropper-hidden"),options.center||$cropBox.find(".cropper-center").addClass("cropper-hidden"),options.cropBoxMovable&&$face.addClass("cropper-move").data("action","all"),options.highlight||$face.addClass("cropper-invisible"),options.background&&$cropper.addClass("cropper-bg"),options.cropBoxResizable||$cropBox.find(".cropper-line, .cropper-point").addClass("cropper-hidden"),self.setDragMode(options.dragMode),self.render(),self.ready=!0,self.setData(options.data),self.completing=setTimeout(function(){$.isFunction(options.ready)&&$this.one("ready",options.ready),self.trigger("ready"),self.trigger("crop",self.getData()),self.completed=!0},0)}}},{key:"unbuild",value:function(){var self=this;self.ready&&(self.completed||clearTimeout(self.completing),self.ready=!1,self.completed=!1,self.initialImage=null,self.initialCanvas=null,self.initialCropBox=null,self.container=null,self.canvas=null,self.cropBox=null,self.unbind(),self.resetPreview(),self.$preview=null,self.$viewBox=null,self.$cropBox=null,self.$dragBox=null,self.$canvas=null,self.$container=null,self.$cropper.remove(),self.$cropper=null)}}],[{key:"setDefaults",value:function(options){$.extend(DEFAULTS,$.isPlainObject(options)&&options)}}]),Cropper}();$.extend(Cropper.prototype,render),$.extend(Cropper.prototype,preview),$.extend(Cropper.prototype,events),$.extend(Cropper.prototype,handlers),$.extend(Cropper.prototype,change),$.extend(Cropper.prototype,methods);var OtherCropper=$.fn.cropper;$.fn.cropper=function(option){for(var _len=arguments.length,args=Array(_len>1?_len-1:0),_key=1;_key<_len;_key++)args[_key-1]=arguments[_key];var result=void 0;return this.each(function(i,element){var $this=$(element),data=$this.data("cropper");if(!data){if(/destroy/.test(option))return;var options=$.extend({},$this.data(),$.isPlainObject(option)&&option);$this.data("cropper",data=new Cropper(element,options))}if("string"==typeof option){var fn=data[option];$.isFunction(fn)&&(result=fn.apply(data,args))}}),void 0!==result?result:this},$.fn.cropper.Constructor=Cropper,$.fn.cropper.setDefaults=Cropper.setDefaults,$.fn.cropper.noConflict=function(){return $.fn.cropper=OtherCropper,this},exports("cropper",$.fn.cropper)}),define("plugins/lay-module/cropper/cropper",function(){}),define("plugins/require-css/css.min!plugins/lay-module/cropper/cropper",[],function(){}),layui.define(["jquery","cropper"],function(exports){var $=layui.jquery;exports("croppers",{render:function(e){var elem=e.elem,saveW=e.saveW,saveH=e.saveH,mark=e.mark,area=e.area,url=e.url,done=e.done;elem_this="#"+elem.attr("id")+"_croppers",id=elem_this.replace("#",""),elem_this+=".croppersbox";var html='';0==$(elem_this).length&&$("body").append(html),layui.form.render();var content=$(elem_this),image=$(elem_this+" .readyimg img"),preview=elem_this+" .img-preview",options={aspectRatio:mark,preview:preview,viewMode:1};elem.on("click",function(){winHeight=$(document).height();var index=layui.layer.open({type:1,title:"裁剪",content:content,maxmin:!0,area:area,move:!0,shadeClose:!0,scoll:!0,resize:!0,success:function(layero){image.cropper(options)},cancel:function(index){content.css("display","none"),image.cropper("destroy"),layer.close(index)},end:function(){content.css("display","none"),image.cropper("destroy"),layer.close(index)},full:function(layero,index){},restore:function(layero,index){}})}),$(elem_this+" .layui-btn").on("click",function(){var event=$(this).attr("cropper-event"),src=$(this).parents(".croppersbox").find(".readyimg img").attr("src");if("confirmSave"===event)return!!src&&(image.cropper("getCroppedCanvas",{width:saveW,height:saveH}).toBlob(function(blob){var formData=new FormData;formData.append("file",blob,"fun-avatar.png"),$.ajax({method:"post",url:url,data:formData,processData:!1,contentType:!1,success:function(result){return result.code>0&&(layer.closeAll("page"),$(".croppersbox").hide()),done(result)}})}),!1);if("rotate"===event){if(!src)return!1;var option=$(this).data("option");image.cropper("rotate",option)}else if("reset"===event){if(!src)return!1;image.cropper("reset")}else if("large"===event){if(!src)return!1;image.cropper("zoom",.1)}else if("small"===event){if(!src)return!1;image.cropper("zoom",-.1)}else if("setDragMode"===event){if(!src)return!1;image.cropper("setDragMode","move")}else if("setDragMode1"===event){if(!src)return!1;image.cropper("setDragMode","crop")}$(this).next("input[name='file']").change(function(){var r=new FileReader,f=this.files[0];r.readAsDataURL(f),r.onload=function(e){image.cropper("destroy").attr("src",this.result).cropper(options)}})})}})}),define("croppers",["plugins/lay-module/cropper/cropper","css!plugins/lay-module/cropper/cropper.css"],function(global){return function(){return global.cropper}}(this)),define("plugins/require-css/css.min!plugins/lay-module/autoComplete/autoComplete",[],function(){});var e,t;e=this,t=function(){"use strict";function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function t(t){for(var n=1;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n=n}(s=e.query?e.query(s):s,e.trigger,e.threshold)?d(e,s).then(function(n){try{return e.feedback instanceof Error?i():(h(s,e),e.resultsList&&function(e){var n=e.resultsList,r=e.list,i=e.resultItem,o=e.feedback,s=o.matches,u=o.results;if(e.cursor=-1,r.innerHTML="",s.length||n.noResults){var c=new DocumentFragment;u.forEach(function(e,n){var r=a(i.tag,t({id:"".concat(i.id,"_").concat(n),role:"option",innerHTML:e.match,inside:c},i.class&&{class:i.class}));i.element&&i.element(r,e)}),r.append(c),n.element&&n.element(r,o),g(e)}else w(e)}(e),c.call(r))}catch(e){return o(e)}},o):(w(e),c.call(r))})}function E(e){var n=this;return new Promise(function(r,i){function c(){return f("init",e),r()}var o,s,u;return o=e.placeHolder,u={role:"combobox","aria-owns":(s=e.resultsList).id,"aria-haspopup":!0,"aria-expanded":!1},a(e.input,t(t({"aria-controls":s.id,"aria-autocomplete":"both"},o&&{placeholder:o}),!e.wrapper&&t({},u))),e.wrapper&&(e.wrapper=a("div",t({around:e.input,class:e.name+"_wrapper"},u))),s&&(e.list=a(s.tag,t({dest:[s.destination,s.position],id:s.id,role:"listbox",hidden:"hidden"},s.class&&{class:s.class}))),T(e),e.data.cache?d(e).then(function(e){try{return c.call(n)}catch(e){return i(e)}},i):c.call(n)})}function x(e){var t=e.prototype;t.init=function(){E(this)},t.start=function(e){j(this,e)},t.unInit=function(){if(this.wrapper){var e=this.wrapper.parentNode;e.insertBefore(this.input,this.wrapper),e.removeChild(this.wrapper)}var t;S((t=this).events,function(e,n){t[e].removeEventListener(n,t.events[e][n])})},t.open=function(){g(this)},t.close=function(){w(this)},t.goTo=function(e){O(e,this)},t.next=function(){A(this)},t.previous=function(){k(this)},t.select=function(e){L(this,null,e)},t.search=function(e,t,n){return p(e,t,n)}}var u=function(e){return"string"==typeof e?document.querySelector(e):e()},a=function(e,t){var n="string"==typeof e?document.createElement(e):e;for(var r in t){var i=t[r];if("inside"===r)i.append(n);else if("dest"===r)u(i[0]).insertAdjacentElement(i[1],n);else if("around"===r){var o=i;o.parentNode.insertBefore(n,o),n.append(o),null!=o.getAttribute("autofocus")&&o.focus()}else r in n?n[r]=i:n.setAttribute(r,i)}return n},c=function(e,t){return e=String(e).toLowerCase(),t?e.normalize("NFD").replace(/[\u0300-\u036f]/g,"").normalize("NFC"):e},l=function(e,n){return a("mark",t({innerHTML:e},"string"==typeof n&&{class:n})).outerHTML},f=function(e,t){t.input.dispatchEvent(new CustomEvent(e,{bubbles:!0,detail:t.feedback,cancelable:!0}))},p=function(e,t,n){var r=n||{},i=r.mode,o=r.diacritics,s=r.highlight,u=c(t,o);if(t=String(t),e=c(e,o),"loose"===i){var a=(e=e.replace(/ /g,"")).length,f=0,p=Array.from(t).map(function(t,n){return f=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var s,u=!0,a=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return u=e.done,e},e:function(e){a=!0,s=e},f:function(){try{u||null==n.return||n.return()}finally{if(a)throw s}}}}(n.keys);try{for(l.s();!(c=l.n()).done;)a(c.value)}catch(e){l.e(e)}finally{l.f()}}else a()}),n.filter&&(i=n.filter(i));var s=i.slice(0,t.resultsList.maxResults);t.feedback={query:e,matches:i,results:s},f("results",t)},m="aria-expanded",b="aria-activedescendant",y="aria-selected",v=function(e,n){e.feedback.selection=t({index:n},e.feedback.results[n])},g=function(e){e.isOpen||((e.wrapper||e.input).setAttribute(m,!0),e.list.removeAttribute("hidden"),e.isOpen=!0,f("open",e))},w=function(e){e.isOpen&&((e.wrapper||e.input).setAttribute(m,!1),e.input.setAttribute(b,""),e.list.setAttribute("hidden",""),e.isOpen=!1,f("close",e))},O=function(e,t){var n=t.resultItem,r=t.list.getElementsByTagName(n.tag),o=!!n.selected&&n.selected.split(" ");if(t.isOpen&&r.length){var s,u,a=t.cursor;e>=r.length&&(e=0),e<0&&(e=r.length-1),t.cursor=e,a>-1&&(r[a].removeAttribute(y),o&&(u=r[a].classList).remove.apply(u,i(o))),r[e].setAttribute(y,!0),o&&(s=r[e].classList).add.apply(s,i(o)),t.input.setAttribute(b,r[t.cursor].id),t.list.scrollTop=r[e].offsetTop-t.list.clientHeight+r[e].clientHeight+5,t.feedback.cursor=t.cursor,v(t,e),f("navigate",t)}},A=function(e){O(e.cursor+1,e)},k=function(e){O(e.cursor-1,e)},L=function(e,t,n){(n=n>=0?n:e.cursor)<0||(e.feedback.event=t,v(e,n),f("selection",e),w(e))},S=function(e,t){for(var n in e)for(var r in e[n])t(n,r)},T=function(e){var n,r,i,o=e.events,s=(n=function(){return j(e)},r=e.debounce,function(){clearTimeout(i),i=setTimeout(function(){return n()},r)}),u=e.events=t({input:t({},o&&o.input)},e.resultsList&&{list:o?t({},o.list):{}}),a={input:{input:function(){s()},keydown:function(t){!function(e,t){switch(e.keyCode){case 40:case 38:e.preventDefault(),40===e.keyCode?A(t):k(t);break;case 13:t.submit||e.preventDefault(),t.cursor>=0&&L(t,e);break;case 9:t.resultsList.tabSelect&&t.cursor>=0&&L(t,e);break;case 27:t.input.value="",w(t)}}(t,e)},blur:function(){w(e)}},list:{mousedown:function(e){e.preventDefault()},click:function(t){!function(e,t){var n=t.resultItem.tag.toUpperCase(),r=Array.from(t.list.querySelectorAll(n)),i=e.target.closest(n);i&&i.nodeName===n&&L(t,e,r.indexOf(i))}(t,e)}}};S(a,function(t,n){(e.resultsList||"input"===n)&&(u[t][n]||(u[t][n]=a[t][n]))}),S(u,function(t,n){e[t].addEventListener(n,u[t][n])})};return function e(t){this.options=t,this.id=e.instances=(e.instances||0)+1,this.name="autoComplete",this.wrapper=1,this.threshold=1,this.debounce=0,this.resultsList={position:"afterend",tag:"ul",maxResults:5},this.resultItem={tag:"li"},function(e){var t=e.name,r=e.options,i=e.resultsList,o=e.resultItem;for(var s in r)if("object"===n(r[s]))for(var a in e[s]||(e[s]={}),r[s])e[s][a]=r[s][a];else e[s]=r[s];e.selector=e.selector||"#"+t,i.destination=i.destination||e.selector,i.id=i.id||t+"_list_"+e.id,o.id=o.id||t+"_result",e.input=u(e.selector)}(this),x.call(this,e),E(this)}},"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define("autoComplete",t):(e="undefined"!=typeof globalThis?globalThis:e||self).autoComplete=t(),function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define("Sortable",e):(t=t||self).Sortable=e()}(this,function(){"use strict";function e(e,t){var n,o=Object.keys(e);return Object.getOwnPropertySymbols&&(n=Object.getOwnPropertySymbols(e),t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),o.push.apply(o,n)),o}function M(o){for(var t=1;tt.length)&&(e=t.length);for(var n=0,o=new Array(e);n"===e[0]&&(e=e.substring(1)),t))try{if(t.matches)return t.matches(e);if(t.msMatchesSelector)return t.msMatchesSelector(e);if(t.webkitMatchesSelector)return t.webkitMatchesSelector(e)}catch(t){return}}function N(t,e,n,o){if(t){n=n||document;do{if(null!=e&&(">"!==e[0]||t.parentNode===n)&&p(t,e)||o&&t===n)return t}while(t!==n&&(t=(i=t).host&&i!==document&&i.host.nodeType?i.host:i.parentNode))}var i;return null}function I(t,e,n){var o;t&&e&&(t.classList?t.classList[n?"add":"remove"](e):(o=(" "+t.className+" ").replace(m," ").replace(" "+e+" "," "),t.className=(o+(n?" "+e:"")).replace(m," ")))}function P(t,e,n){var o=t&&t.style;if(o){if(void 0===n)return document.defaultView&&document.defaultView.getComputedStyle?n=document.defaultView.getComputedStyle(t,""):t.currentStyle&&(n=t.currentStyle),void 0===e?n:n[e];o[e=e in o||-1!==e.indexOf("webkit")?e:"-webkit-"+e]=n+("string"==typeof n?"":"px")}}function v(t,e){var n="";if("string"==typeof t)n=t;else do{var o=P(t,"transform")}while(o&&"none"!==o&&(n=o+" "+n),!e&&(t=t.parentNode));var i=window.DOMMatrix||window.WebKitCSSMatrix||window.CSSMatrix||window.MSCSSMatrix;return i&&new i(n)}function b(t,e,n){if(t){var o=t.getElementsByTagName(e),i=0,r=o.length;if(n)for(;i=n.left-e&&i<=n.right+e,e=r>=n.top-e&&r<=n.bottom+e;return o&&e?a=t:void 0}}),a);if(e){var n,o={};for(n in t)t.hasOwnProperty(n)&&(o[n]=t[n]);o.target=o.rootEl=e,o.preventDefault=void 0,o.stopPropagation=void 0,e[j]._onDragOver(o)}}var i,r,a}function Yt(t){q&&q.parentNode[j]._isOutsideThisEl(t.target)}function Bt(t,e){if(!t||!t.nodeType||1!==t.nodeType)throw"Sortable: `el` must be an HTMLElement, not ".concat({}.toString.call(t));this.el=t,this.options=e=a({},e),t[j]=this;var n,o,i={group:null,sort:!0,disabled:!1,store:null,handle:null,draggable:/^[uo]l$/i.test(t.nodeName)?">li":">*",swapThreshold:1,invertSwap:!1,invertedSwapThreshold:null,removeCloneOnHide:!0,direction:function(){return It(t,this.options)},ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",ignore:"a, img",filter:null,preventOnFilter:!0,animation:0,easing:null,setData:function(t,e){t.setData("Text",e.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id",delay:0,delayOnTouchOnly:!1,touchStartThreshold:(Number.parseInt?Number:window).parseInt(window.devicePixelRatio,10)||1,forceFallback:!1,fallbackClass:"sortable-fallback",fallbackOnBody:!1,fallbackTolerance:0,fallbackOffset:{x:0,y:0},supportPointer:!1!==Bt.supportPointer&&"PointerEvent"in window&&!u,emptyInsertThreshold:5};for(n in K.initializePlugins(this,t,i),i)n in e||(e[n]=i[n]);for(o in Pt(e),this)"_"===o.charAt(0)&&"function"==typeof this[o]&&(this[o]=this[o].bind(this));this.nativeDraggable=!e.forceFallback&&Mt,this.nativeDraggable&&(this.options.touchStartThreshold=1),e.supportPointer?h(t,"pointerdown",this._onTapStart):(h(t,"mousedown",this._onTapStart),h(t,"touchstart",this._onTapStart)),this.nativeDraggable&&(h(t,"dragover",this),h(t,"dragenter",this)),Et.push(this.el),e.store&&e.store.get&&this.sort(e.store.get(this)||[]),a(this,x())}function Ft(t,e,n,o,i,r,a,l){var s,c,u=t[j],d=u.options.onMove;return!window.CustomEvent||y||w?(s=document.createEvent("Event")).initEvent("move",!0,!0):s=new CustomEvent("move",{bubbles:!0,cancelable:!0}),s.to=e,s.from=t,s.dragged=n,s.draggedRect=o,s.related=i||e,s.relatedRect=r||k(e),s.willInsertAfter=l,s.originalEvent=a,t.dispatchEvent(s),c=d?d.call(u,s,a):c}function jt(t){t.draggable=!1}function Ht(){Ct=!1}function Lt(t){return setTimeout(t,0)}function Kt(t){return clearTimeout(t)}function Qt(){Zt.forEach(function(t){clearInterval(t.pid)}),Zt=[]}function Jt(){clearInterval(Vt)}function ne(){}function oe(){}function pe(n,o){ue.forEach(function(t,e){e=o.children[t.sortableIndex+(n?Number(e):0)],e?o.insertBefore(t,e):o.appendChild(t)})}function ge(){ce.forEach(function(t){t!==ae&&t.parentNode&&t.parentNode.removeChild(t)})}var g,q,V,Z,$,Q,J,tt,et,nt,ot,it,rt,at,lt,st,ct,ut,dt,ht,ft,pt,gt,mt,vt,bt,y=t(/(?:Trident.*rv[ :]?11\.|msie|iemobile|Windows Phone)/i),w=t(/Edge/i),s=t(/firefox/i),u=t(/safari/i)&&!t(/chrome/i)&&!t(/android/i),n=t(/iP(ad|od|hone)/i),c=t(/chrome/i)&&t(/android/i),d={capture:!1,passive:!1},m=/\s+/g,j="Sortable"+(new Date).getTime(),H=[],L={initializeByDefault:!0},K={mount:function(e){for(var t in L)!L.hasOwnProperty(t)||t in e||(e[t]=L[t]);H.forEach(function(t){if(t.pluginName===e.pluginName)throw"Sortable: Cannot mount plugin ".concat(e.pluginName," more than once")}),H.push(e)},pluginEvent:function(e,n,o){var t=this;this.eventCanceled=!1,o.cancel=function(){t.eventCanceled=!0};var i=e+"Global";H.forEach(function(t){n[t.pluginName]&&(n[t.pluginName][i]&&n[t.pluginName][i](M({sortable:n},o)),n.options[t.pluginName]&&n[t.pluginName][e]&&n[t.pluginName][e](M({sortable:n},o)))})},initializePlugins:function(n,o,i,t){for(var e in H.forEach(function(t){var e=t.pluginName;(n.options[e]||t.initializeByDefault)&&((t=new t(n,o,n.options)).sortable=n,t.options=n.options,n[e]=t,a(i,t.defaults))}),n.options){var r;n.options.hasOwnProperty(e)&&void 0!==(r=this.modifyOption(n,e,n.options[e]))&&(n.options[e]=r)}},getEventProperties:function(e,n){var o={};return H.forEach(function(t){"function"==typeof t.eventProperties&&a(o,t.eventProperties.call(n[t.pluginName],e))}),o},modifyOption:function(e,n,o){var i;return H.forEach(function(t){e[t.pluginName]&&t.optionListeners&&"function"==typeof t.optionListeners[n]&&(i=t.optionListeners[n].call(e[t.pluginName],o))}),i}},G=["evt"],yt=!1,wt=!1,Et=[],Dt=!1,St=!1,_t=[],Ct=!1,Tt=[],xt="undefined"!=typeof document,Ot=n,At=w||y?"cssFloat":"float",Mt=xt&&!c&&!n&&"draggable"in document.createElement("div"),Nt=function(){if(xt){if(y)return!1;var t=document.createElement("x");return t.style.cssText="pointer-events:auto","auto"===t.style.pointerEvents}}(),It=function(t,e){var n=P(t),o=parseInt(n.width)-parseInt(n.paddingLeft)-parseInt(n.paddingRight)-parseInt(n.borderLeftWidth)-parseInt(n.borderRightWidth),i=X(t,0,e),r=X(t,1,e),a=i&&P(i),l=r&&P(r),s=a&&parseInt(a.marginLeft)+parseInt(a.marginRight)+k(i).width,t=l&&parseInt(l.marginLeft)+parseInt(l.marginRight)+k(r).width;return"flex"===n.display?"column"===n.flexDirection||"column-reverse"===n.flexDirection?"vertical":"horizontal":"grid"===n.display?n.gridTemplateColumns.split(" ").length<=1?"vertical":"horizontal":i&&a.float&&"none"!==a.float?(e="left"===a.float?"left":"right",!r||"both"!==l.clear&&l.clear!==e?"horizontal":"vertical"):i&&("block"===a.display||"flex"===a.display||"table"===a.display||"grid"===a.display||o<=s&&"none"===n[At]||r&&"none"===n[At]&&o=Math.floor(this.options.touchStartThreshold/(this.nativeDraggable&&window.devicePixelRatio||1))&&this._disableDelayedDrag()},_disableDelayedDrag:function(){q&&jt(q),clearTimeout(this._dragStartTimer),this._disableDelayedDragEvents()},_disableDelayedDragEvents:function(){var t=this.el.ownerDocument;f(t,"mouseup",this._disableDelayedDrag),f(t,"touchend",this._disableDelayedDrag),f(t,"touchcancel",this._disableDelayedDrag),f(t,"mousemove",this._delayedDragTouchMoveHandler),f(t,"touchmove",this._delayedDragTouchMoveHandler),f(t,"pointermove",this._delayedDragTouchMoveHandler)},_triggerDragStart:function(t,e){e=e||"touch"==t.pointerType&&t,!this.nativeDraggable||e?this.options.supportPointer?h(document,"pointermove",this._onTouchMove):h(document,e?"touchmove":"mousemove",this._onTouchMove):(h(q,"dragend",this),h($,"dragstart",this._onDragStart));try{document.selection?Lt(function(){document.selection.empty()}):window.getSelection().removeAllRanges()}catch(t){}},_dragStarted:function(t,e){var n;yt=!1,$&&q?(z("dragStarted",this,{evt:e}),this.nativeDraggable&&h(document,"dragover",Yt),n=this.options,t||I(q,n.dragClass,!1),I(q,n.ghostClass,!0),Bt.active=this,t&&this._appendGhost(),U({sortable:this,name:"start",originalEvent:e})):this._nulling()},_emulateDragOver:function(){if(ct){this._lastX=ct.clientX,this._lastY=ct.clientY,kt();for(var t=document.elementFromPoint(ct.clientX,ct.clientY),e=t;t&&t.shadowRoot&&(t=t.shadowRoot.elementFromPoint(ct.clientX,ct.clientY))!==e;)e=t;if(q.parentNode[j]._isOutsideThisEl(t),e)do{if(e[j]&&e[j]._onDragOver({clientX:ct.clientX,clientY:ct.clientY,target:t,rootEl:e})&&!this.options.dragoverBubble)break}while(e=(t=e).parentNode);Rt()}},_onTouchMove:function(t){if(st){var e=this.options,n=e.fallbackTolerance,o=e.fallbackOffset,i=t.touches?t.touches[0]:t,r=Z&&v(Z,!0),a=Z&&r&&r.a,l=Z&&r&&r.d,e=Ot&&bt&&E(bt),a=(i.clientX-st.clientX+o.x)/(a||1)+(e?e[0]-_t[0]:0)/(a||1),l=(i.clientY-st.clientY+o.y)/(l||1)+(e?e[1]-_t[1]:0)/(l||1);if(!Bt.active&&!yt){if(n&&Math.max(Math.abs(i.clientX-this._lastX),Math.abs(i.clientY-this._lastY))n.right+10||t.clientX<=n.right&&t.clientY>n.bottom&&t.clientX>=n.left:t.clientX>n.right&&t.clientY>n.top||t.clientX<=n.right&&t.clientY>n.bottom+10}(n,r,this)&&!g.animated){if(g===q)return O(!1);if((l=g&&a===n.target?g:l)&&(w=k(l)),!1!==Ft($,a,q,o,l,w,n,!!l))return x(),g&&g.nextSibling?a.insertBefore(q,g.nextSibling):a.appendChild(q),V=a,A(),O(!0)}else if(g&&function(t,e,n){return n=k(X(n.el,0,n.options,!0)),e?t.clientX=e?t:""+Array(e+1-r.length).join(n)+t},v={s:m,z:function(t){var e=-t.utcOffset(),n=Math.abs(e),r=Math.floor(n/60),i=n%60;return(e<=0?"+":"-")+m(r,2,"0")+":"+m(i,2,"0")},m:function t(e,n){if(e.date()1)return t(u[0])}else{var a=e.name;D[a]=e,i=a}return!r&&i&&(g=i),i||!r&&g},w=function(t,e){if(p(t))return t.clone();var n="object"==typeof e?e:{};return n.date=t,n.args=arguments,new _(n)},O=v;O.l=S,O.i=p,O.w=function(t,e){return w(t,{locale:e.$L,utc:e.$u,x:e.$x,$offset:e.$offset})};var _=function(){function M(t){this.$L=S(t.locale,null,!0),this.parse(t)}var m=M.prototype;return m.parse=function(t){this.$d=function(t){var e=t.date,n=t.utc;if(null===e)return new Date(NaN);if(O.u(e))return new Date;if(e instanceof Date)return new Date(e);if("string"==typeof e&&!/Z$/i.test(e)){var r=e.match($);if(r){var i=r[2]-1||0,s=(r[7]||"0").substring(0,3);return n?new Date(Date.UTC(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)):new Date(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)}}return new Date(e)}(t),this.$x=t.x||{},this.init()},m.init=function(){var t=this.$d;this.$y=t.getFullYear(),this.$M=t.getMonth(),this.$D=t.getDate(),this.$W=t.getDay(),this.$H=t.getHours(),this.$m=t.getMinutes(),this.$s=t.getSeconds(),this.$ms=t.getMilliseconds()},m.$utils=function(){return O},m.isValid=function(){return!(this.$d.toString()===l)},m.isSame=function(t,e){var n=w(t);return this.startOf(e)<=n&&n<=this.endOf(e)},m.isAfter=function(t,e){return w(t)>16)+(y>>16)+(a>>16)<<16|65535&a}function bit_rol(a,b){return a<>>32-b}function md5_cmn(q,a,b,x,s,t){return safe_add(bit_rol(safe_add(safe_add(a,q),safe_add(x,t)),s),b)}function md5_ff(a,b,c,d,x,s,t){return md5_cmn(b&c|~b&d,a,b,x,s,t)}function md5_gg(a,b,c,d,x,s,t){return md5_cmn(b&d|c&~d,a,b,x,s,t)}function md5_hh(a,b,c,d,x,s,t){return md5_cmn(b^c^d,a,b,x,s,t)}function md5_ii(a,b,c,d,x,s,t){return md5_cmn(c^(b|~d),a,b,x,s,t)}function binl_md5(x,e){x[e>>5]|=128<>>9<<4)]=e;var i,olda,oldb,oldc,oldd,a=1732584193,b=-271733879,c=-1732584194,d=271733878;for(i=0;i>5]>>>i%32&255);return output}function rstr2binl(a){var i,output=[];for(output[(a.length>>2)-1]=void 0,i=0;i>5]|=(255&a.charCodeAt(i/8))<16&&(bkey=binl_md5(bkey,8*a.length)),i=0;i<16;i+=1)ipad[i]=909522486^bkey[i],opad[i]=1549556828^bkey[i];return hash=binl_md5(ipad.concat(rstr2binl(b)),512+8*b.length),binl2rstr(binl_md5(opad.concat(hash),640))}function rstr2hex(a){var x,i,b="0123456789abcdef",output="";for(i=0;i>>4&15)+b.charAt(15&x);return output}function str2rstr_utf8(a){return unescape(encodeURIComponent(a))}function raw_md5(s){return rstr_md5(str2rstr_utf8(s))}function hex_md5(s){return rstr2hex(raw_md5(s))}function raw_hmac_md5(k,d){return rstr_hmac_md5(str2rstr_utf8(k),str2rstr_utf8(d))}function hex_hmac_md5(k,d){return rstr2hex(raw_hmac_md5(k,d))}function md5(a,b,c){return b?c?raw_hmac_md5(b,a):hex_hmac_md5(b,a):c?raw_md5(a):hex_md5(a)}"function"==typeof define&&define.amd?define("md5",[],function(){return md5}):$.md5=md5}(this),define("fun",["lang","toastr","dayjs"],function(Lang,Toastr,Dayjs){var $=layui.jquery,layer=layui.layer,element=layui.element;layer=layer||parent.layer,layui.layer.config({skin:"fun-layer-class"}),Toastr=parent.Toastr||Toastr;var Fun={url:function(url){url=void 0==url?location.href:url;var domain=window.location.host;if(url&&-1!==url.indexOf(domain))return url;var ajax_url=Config.publicAjaxUrl;tempurl=url.split("?");var suffix=tempurl[1]?"?"+tempurl[1]:"",prefix=tempurl[0];if(-1!==$.inArray(prefix,ajax_url))return Config.module+url;prefix=0==prefix.indexOf("/")?prefix.replace("/",""):prefix,prefix=Fun.common.parseNodeStr(prefix);var n=prefix.split("/").length-1;return url=0==n?"/"+Config.appname+"/"+Config.controllername+"/"+prefix+suffix:1==n?"/"+Config.appname+"/"+prefix+suffix:"/"+prefix+suffix},replaceurl:function(url,d){return id=void 0!==d.primaryKeyValue?d.primaryKeyValue:0,url&&(url=-1!==url.indexOf("{ids}")?url.replace("{ids}",id):url),url},checkAuth:function(node,ele){return!0===Config.superAdmin||(node.indexOf("?")>=0&&(node=node.replace(/([?#])[^'"]*/,"")),1===$(ele).data("node-"+node.toLowerCase()))},param:function(param,defaultParam){return void 0!==param?param:defaultParam},refreshmenu:function(){top.window.$("#layui-side-left-menu").trigger("refresh")},ajax:function(option,success,error,ex){return option.method=option.method||"post",option.tips=option.tips||"",option.url=option.url||"",option.data=option.data||{},option.statusName=option.statusName||"code",option.statusCode=option.statusCode||1,success=success||function(res){var msg=void 0===res.msg&&void 0===res.message?__("Return data is not right"):res.msg?res.msg:res.message;return Fun.toastr.success(msg),Fun.toastr.close(),!1},error=error||function(res){var msg=void 0===res.msg&&void 0===res.message?__("Return data is not right"):res.msg?res.msg:res.message;return Fun.toastr.error(msg),!1},ex=ex||function(res){},""===option.url?(Fun.toastr.error(__("Request url can not empty")),!1):(option.async=!1!==option.async||option.async,$.ajax({url:option.url,type:option.method,contentType:"application/x-www-form-urlencoded; charset=UTF-8",dataType:"json",data:option.data,async:option.async,timeout:0,headers:{"X-CSRF-TOKEN":$('meta[name="csrf-token"]').prop("content")},beforeSend:function(xhr,request){request.url=Fun.url(request.url)},success:function(res){return eval("res."+option.statusName)>=option.statusCode?success(res):error(res)},error:function(xhr){var message=void 0!==xhr.responseJSON?__(xhr.responseJSON.message):__(",Try again later!");return Fun.toastr.error("Status:"+xhr.status+"\n"+message,function(){var token=xhr.getResponseHeader("__token__");token&&$("input[name='__token__']").val(token),ex(this)}),!1},complete:function(xhr){var token=xhr.getResponseHeader("__token__");token&&$("input[name='__token__']").val(token)}}),!0)},lang:function(){var obj=arguments,str=arguments[0];if(null!==str&&""!==str&&(str=str.toString(),str=str.toLowerCase()),str=void 0!==Lang&&void 0!==Lang[str]?Lang[str]:obj[0],"object"==typeof obj[1])for(i=0;i0?$(".layui-layer").each(function(){index=Math.max(index,parseInt($(this).attr("times")))}):parent.$(".layui-layer").length&&parent.$(".layui-layer").each(function(){index=Math.max(index,parseInt($(this).attr("times")))}),index)var confirm=top.layui.layer.confirm(__("are you sure you want to close this window"),{btn:[__("confirm"),__("cancel")]},function(){layui.layer.close(index),top.layui.layer.close(confirm),parent.layui.layer.close(index)},function(){})}),$("#lock-screen").length>0&&!Fun.api.getStorage("BackendLock")&&$("#lock-screen").remove()}})},bindEvent:function(formCallback,success,error,ex){},common:{parseNodeStr:function(node){if(node&&-1===node.indexOf("/")&&(node=Config.controllername+"/"+node),void 0!==node){var arrayNode=node.split("/");return layui.each(arrayNode,function(key,val){0===key&&(val=val.split("."),layui.each(val,function(i,v){v=Fun.common.lower(Fun.common.snake(v)),val[i]=v.slice(0,1).toLowerCase()+v.slice(1)}),val=val.join("."),arrayNode[key]=Fun.common.camel(val))}),node=arrayNode.join("/")}return""},camel:function(name){return name.replace(/_(\w)/g,function(all,letter){return letter.toUpperCase()})},snake:function(name){return name.replace(/([A-Z])/g,"_$1").toLowerCase()},lower:function(name){return name.toLowerCase()},arrTostr:function(arr){var str="";for(i=0;i=0?url=url+"&"+ele.primaryKey+"="+d[ele.primaryKey]:url=url+"?"+ele.primaryKey+"="+d[ele.primaryKey]),Fun.checkAuth(node,ele.elem||tableOption.elem)&&(dropdowndata[k]=extend[k],dropdowndata[k].rowindex=k,dropdowndata[k].buttonsindex=data.buttonsindex,dropdowndata[k].url=url,dropdowndata[k].class=v.class||"",dropdowndata[k].id=v.id||v.event,dropdowndata[k].callback=v.callback||"",dropdowndata[k].extend=v.extend||"",dropdowndata[k].type=v.type||"normal",dropdowndata[k].target=v.target||"_self",dropdowndata[k].child=v.child||[],dropdowndata[k].textTitle=v.title,dropdowndata[k].icon=v.icon||"",dropdowndata[k].field=v.field||"",dropdowndata[k].value=v.value||"",icon=extend[k].icon?'':"",dropdowndata[k].templet=v.templet||""+icon+" {{d.title}} ",dropdowndata[k].title=v.title)});var inst=layui.dropdown.render({elem:othis,show:!0,data:dropdowndata,click:function(row,_t){attrEvent=row.event,data.title=row.textTitle,data.rowindex=row.rowindex,buttons=rowData?rowData.data.buttons:tableOption.buttons,buttons=Fun.api.getButtons(buttons,data.buttonsindex,data.rowindex),callback=buttons.extend[data.rowindex].callback,require(["table"],function(Table){Table.events.hasOwnProperty(attrEvent)?Table.events[attrEvent].call(this,_t.find("a"),data,rowData,tableOption):data.callback?eval(data.callback)(_t,data,rowData,tableOption):"function"==typeof callback?callback(_t,data,rowData,tableOption):callback&&"string"==typeof callback?eval(callback)(_t,data,rowData,tableOption):eval(data.event)(_t,data,rowData,tableOption)})},style:"box-shadow: 1px 1px 10px rgb(0 0 0 / 12%);"});return inst.reload(),!1}}},api:{removeEmptyData:function(obj){return $.each(obj,function(key,value){null===value||void 0===value||""===value?delete obj[key]:"object"==typeof value&&Fun.api.removeEmptyData(value)}),obj},mergeFunc:function(func1,func2){return function(){func1.apply(this,arguments),func2.apply(this,arguments)}},setStorage:function(key,value){null!=value&&"undefined"!==value?layui.data(key,{key:key,value:value}):layui.data(key,{key:key,remove:!0})},getStorage:function(key){var array=layui.data(key);return!!array&&array[key]},setFrameTheme:function(body){var colorId=Fun.api.getStorage("funColorId");if(colorId=colorId||0,theme="theme"+colorId,Fun.api.getStorage("setFrameTheme")){for(var iframe=$("#layui-tab .layui-tab-item").find("iframe"),i=0;i768)},checkLayerIframe:function(){return!!parent.$(".layui-layer").length},open:function(options){var title=options.title,url=options.url,width=options.width,height=options.height,success=options.success,cancel=options.cancel,end=options.end,yes=options.yes,type=options.type,autoheight=options.autoheight;type=void 0===type||2===type?2:1;var isResize=void 0===options.isResize,isFull=!!options.full;url=2===type?Fun.url(url):url,isResize=!1===isResize||isResize,width=width||Config.site.site_layer_width,height=height||Config.site.site_layer_height,width=width||"50%",height=height||"100%",width=/%|px/.test(width)?width:$(window).width()+20>=width?width+"px":"95%",height=/%|px/.test(height)?height:$(window).height()+110>=height?height+"px":"100%",autoheight=!!autoheight,offset=options.offset||Config.site.site_layer_offset,anim=Config.site.site_layer_anim?Config.site.site_layer_anim:options.anim,offset=offset||"r",anim=anim||"slideLeft",isFull&&(width="100%",height="100%");var btns=[];void 0==options.btn?(btns=["submit","close"],options.btn_lang=[__("submit"),__("close")]):"false"===options.btn||!1===options.btn||""===options.btn?options.btn_lang=!1:(btnsdata=options.btn,btnsdata=layui.isArray(btnsdata)?btnsdata:btnsdata.split(","),options.btn_lang=[],layui.each(btnsdata,function(k,v){options.btn_lang[k]=__(v),btns.push(v)})),void 0===options.btnAlign&&(options.btnAlign="c"),options.btn_lang===[]&&(options.btn_lang=!1);var parentiframe=Fun.api.checkLayerIframe();if(opt=$.extend(options||{},{title:title,type:type,area:[width,height],content:url,shadeClose:!0,offset:offset,anim:anim,shade:.1,isOutAnim:!0,zIndex:parent.layui.layer.zIndex,maxmin:!0,moveOut:!0,resize:isResize,scrollbar:!0,btnAlign:options.btnAlign,btn:options.btn_lang,success:void 0===success?function(layero,index){var that=this;try{$(layero).data("callback",that.callback?that.callback:""),parent.layui.layer.setTop(layero),autoheight&&parent.layui.layer.iframeAuto(index),layero.addClass("layui-form"),layero.find(".layui-layer-btn.layui-layer-btn-c").css("background","#f3f6f6"),body=layero.find("iframe").contents().find("body"),Fun.api.setFrameTheme(body)}catch(err){console.log(err)}}:success,yes:void 0===yes?function(index,layero){try{if(1==btns.length&&"close"==btns[0])return layui.ayer.close(index),!1;$(document).ready(function(){var body=layui.layer.getChildFrame("body",index);parentiframe&&(body=parent.layui.layer.getChildFrame("body",index)),body.find('button[type="'+btns[0]+'"]').trigger("click"),body.find(".layui-hide").hide()})}catch(err){layui.layer.close(index)}return!1}:yes,cancel:void 0===cancel?function(index,layero){layui.layer.close(layer.index)}:cancel,end:void 0===end?function(index,layero){layui.layer.close(layer.index)}:end}),btns.length>1)for(i=1;i0){var opt=[],blobSlice=File.prototype.slice||File.prototype.mozSlice||File.prototype.webkitSlice,chunkSize=1024*Upload.init.upload_chunksize*1024,maxSize=1024*Upload.init.upload_size*1024;fileImageType={audio:"mp3|wma|wav",image:"jpg|jpeg|png|gif|svg|bmp|webp",mp3:"mp3|wma|wav",pdf:"pdf",pptx:"ppt|pptx|doc|docx",txt:"txt",video:"mp4|rmvb|avi|ts",word:"word|doc|docx",xlsx:"xls|xlsx",zip:"rar|tar|zip|7z|gz",office:"ppt|pptx|doc|docx|word|doc|docx|xls|xlsx",file:"*"},layui.each(uploadList,function(i,v){var _that=$(this),data=_that.data();"object"==typeof data.value&&(data=data.value);var uploadNum=data.num,id=_that.prop("id")||data.id,uploadMime=data.mime,acceptMime=data.acceptmime||"",uploadAccept=data.accept,uploadPath=data.path||"upload",uploadSize=data.size,save=data.save||0,group=data.group||"",uploadmultiple=data.multiple,uploadExts=data.exts,chunk=data.chunk,uploadNum=uploadNum||1;uploadSize=uploadSize||Upload.init.upload_size,uploadExts=uploadExts||Upload.init.upload_exts,uploadExts=uploadExts.indexOf(",")?uploadExts.replace(/,/g,"|"):uploadExts,uploadmultiple=uploadmultiple||!1,uploadNum=uploadmultiple&&1==uploadNum?100:uploadNum,uploadAccept=uploadAccept||uploadMime||"*",uploadAccept="*"===uploadAccept?"file":uploadAccept;var index,_parent=$(this).parents(".layui-upload"),input=_parent.find('input[type="text"]'),fileList=[],chunkList=[];opt[i]=$.extend({elem:this,accept:uploadAccept,size:1024*uploadSize,number:uploadNum,multiple:uploadmultiple,auto:!1,url:Fun.url(data.url?data.url:Upload.init.requests.upload_url)+"?path="+uploadPath+"&save="+save+"&group_id="+group,before:function(obj){void 0==chunk||0==chunk||0==chunk?index=Fun.toastr.loading(__("uploading"),setTimeout(function(){Fun.toastr.close()},1200)):this.data&&(void 0===this.data.chunkId?(window[this.data.chunkId]=layui.layer.open({type:1,title:!1,skin:"chunkProgress",closeBtn:0,resize:!1,shade:.1,area:["420px","20px"],content:['
                ','
                ',"
                "].join(""),success:function(layerObj,index){layui.layer.setTop(layerObj)}}),layui.element.render()):layui.element.progress("uploadProgress",Math.ceil(100*(1+this.data.chunkIndex)/this.data.chunkCount)+"%"))},progress:void 0===progress?function(n,elem){}:progress,choose:void 0===choose?function(obj){var that=this,files=this.files=obj.pushFile();obj.preview(function(index,file,result){if(file.size>maxSize)return delete files[index],Fun.toastr.error(__("文件大小超过限制,最大不超过"+maxSize/1024+"KB")),!1;if(file.size<=chunkSize||void 0==chunk||0==chunk||0==chunk)console.log(1),obj.upload(index,file),delete files[index];else if(1==chunk||1==chunk){var chunkId=file.lastModified+"-"+file.size,chunkCount=Math.ceil(file.size/chunkSize),fileExt=/\.([0-9A-z]+)$/.exec(file.name)[1],list=[];for(i=0;i0)if(void 0===res.data.chunkId||res.data.url){void 0!==res.data.chunkId&&(layui.layer.close(window[res.data.chunkId]),delete chunkList[res.data.chunkId],delete fileList[res.data.chunkId],res.url=res.data.url);var start=res.url.lastIndexOf(".");ext=res.url.substring(start+1,res.url.length).toLowerCase();var fileType="";for(var key in fileImageType){if(-1!==fileImageType[key].indexOf(ext)){fileType=key;break}if("*"==fileImageType[key]){fileType=key;break}}console.log(fileType),html="image"==fileType?'
              • \n':'
              • \n';var inputVal=input.val();if(1==uploadNum)input.val(res.url),_parent.find(".layui-upload-list").html(html);else if("*"==uploadNum)_parent.find(".layui-upload-list").append(html),val_temp=inputVal?inputVal+","+res.url:res.url,input.val(val_temp);else{if(_parent.find("li").length>=uploadNum)return Fun.toastr.error(__("File nums is limited")),!1;_parent.find(".layui-upload-list").append(html),val_temp=inputVal?inputVal+","+res.url:res.url,input.val(val_temp)}Fun.toastr.success(__("Upload Success"),setTimeout(function(){Fun.toastr.close()},1500))}else{var chunkIndex=res.data.chunkIndex,chunkId=res.data.chunkId,chunkCount=res.data.chunkCount,currentChunkList=chunkList[chunkId][chunkIndex];if(chunkIndex+10){var opt=[];layui.each(cropperlist,function(i){var _that=$(this),_parent=_that.parents(".layui-upload"),id=_that.prop("id")||_that.data("id"),data=_that.data();"object"==typeof data.value&&(data=data.value);var saveW=data.width||300,saveH=data.height||300,mark=data.mark||1,area=data.area,uploadPath=data.path||"upload";opt[i]=$.extend({elem:$(this),saveW:saveW,saveH:saveH,mark:mark,area:area,url:Fun.url(data.url?data.url:Upload.init.requests.upload_url)+"?path="+uploadPath,done:void 0===success?function(res){if(res.code>0){Fun.toastr.success(res.msg),_parent.find('input[type="text"]').val(res.url);var html='
              • \n';_parent.find(".layui-upload-list").html(html)}else res.code<=0&&Fun.toastr.error(res.msg)}:success,error:void 0===error?function(index){}:error},void 0===options?{}:options),window["cropper-"+id]=layui.croppers.render(opt[i])})}},bindEvent:function(){Upload.events.uploads(),Upload.events.cropper()}}});return Upload}),define("form",["upload"],function(Upload){var $=layui.$,Form={init:{},events:{events:function(){list=$("*[lay-event]"),list.length>0&&layui.each(list,function(){$(this).click(function(e){return"open"===$(this).attr("lay-event")?(Fun.events.open($(this)),!0):"request"===$(this).attr("lay-event")?(Fun.events.request($(this)),!0):"iframe"===$(this).attr("lay-event")?(Fun.events.iframe($(this)),!0):"dropdown"===$(this).attr("lay-event")?(Fun.events.dropdown($(this)),!0):void 0})})},autocomplete:function(formObj){Form.api.autocomplete(formObj)},selects:function(formObj){Form.api.selects(formObj)},selectcx:function(formObj){Form.api.selectcx(formObj)},selectpage:function(formObj){Form.api.selectpage(formObj)},xmselect:function(formObj){Form.api.xmselect(formObj)},editor:function(formObj){Form.api.editor(formObj)},tags:function(formObj){Form.api.tags(formObj)},icon:function(formObj){Form.api.icon(formObj)},color:function(formObj){Form.api.color(formObj)},city:function(formObj){Form.api.city(formObj)},timepicker:function(formObj){Form.api.timepicker(formObj)},datepicker:function(formObj){Form.api.datepicker(formObj)},date:function(formObj){Form.api.date(formObj)},rate:function(formObj){Form.api.rate(formObj)},slider:function(formObj){Form.api.slider(formObj)},formarray:function(formObj){Form.api.formarray(formObj)},uploads:function(formObj){Upload.api.uploads()},cropper:function(formObj){Upload.api.cropper()},choosefiles:function(formObj){Form.api.chooseFiles(formObj)},selectfiles:function(formObj){Form.api.selectFiles(formObj)},json:function(formObj){Form.api.json(formObj)},transfer:function(formObj){Form.api.transfer(formObj)},verifys:function(formObj){Form.api.verifys(formObj)},required:function(formObj){Form.api.required(formObj)},submit:function(formObj,success,error,submit){var formList=$("[lay-submit]");formList.length>0&&layui.each(formList,function(i){var filter=$(this).attr("lay-filter"),type=$(this).data("type"),refresh=$(this).data("refresh"),url=$(this).data("request")||$(this).data("url")||$('form[lay-filter="'+filter+'"]').attr("action");if("tableSearch"===type)return!1;void 0===refresh&&(refresh=!0),"false"===refresh&&(refresh=!1),""===refresh&&(refresh=!1),void 0!==filter&&""!==filter||(filter="form-"+(i+1),$(this).attr("lay-filter",filter)),void 0!==url&&""!==url&&null!=url||(url=location.href),layui.form.on("submit("+filter+")",function(data){if($("select[multiple]").length>0){var $select=$("select[multiple]");layui.each($select,function(){var field=$(this).attr("name"),vals=[];$(this).children("option:selected").each(function(){vals.push($(this).val())}),data.field[field]=vals.join(",")})}var dataField=data.field;return"function"==typeof formObj?formObj(url,dataField):"function"==typeof submit?submit(url,dataField):Form.api.formSubmit(url,dataField,success,error,refresh),!1})})},photos:function(otihs){Fun.events.photos(otihs)},filedelete:function(othis){Form.api.filedelete(othis)},bindevent:function(formObj){formObj.on("click","[lay-event]",function(){var _t=$(this),attrEvent=_t.attr("lay-event");Form.events.hasOwnProperty(attrEvent)&&Form.events[attrEvent]&&Form.events[attrEvent].call(this,_t)}),require(["table"],function(Table){$("body").on("click","[lay-event]",function(){if($("table").length>0){var _t=$(this),attrEvent=_t.attr("lay-event");Table.events.hasOwnProperty(attrEvent)&&Table.events[attrEvent]&&Table.events[attrEvent].call(this,_t)}})})}},api:{closeOpen:function(option){option=option||{},option.refreshTable=option.refreshTable||!1,option.refreshFrame=option.refreshFrame||!1;var index=parent.layui.layer.getFrameIndex(window.name);return index&&parent.layui.layer.close(index),!0===option.refreshTable&&require(["table"],function(Table){if(option.refreshTable=option.tableid||Table.init.tableId,!(self!==top&&parent.$("#"+option.refreshTable).length>0))return setTimeout(function(){location.reload()},2e3),!1;Table.api.reload(option.refreshTable)}),!!option.refreshFrame&&(setTimeout(function(){location.reload()},2e3),!1)},formSubmit:function(url,data,success,error,refresh){return success=success||function(res){return res.msg=res.msg||"success",Fun.toastr.success(res.msg),Form.api.closeOpen({refreshTable:refresh,refreshFrame:refresh}),!1},error=error||function(res){return res.msg=res.msg||"error",Fun.toastr.error(res.msg,function(){}),!1},Fun.ajax({url:url,data:data,complete:function(xhr){var token=xhr.getResponseHeader("__token__");token&&$("input[name='__token__']").val(token)}},success,error),!1},required:function(formObj){var vfList=$("[lay-verify]");vfList.length>0&&(layui.each(vfList,function(){var verify=$(this).attr("lay-verify");if(verify&&verify.indexOf("required")>=0){var label=$(this).parent().prev();label.is("label")&&!label.hasClass("required")&&label.addClass("required"),void 0===$(this).attr("lay-reqtext")&&void 0!==$(this).attr("placeholder")&&$(this).attr("lay-reqtext",$(this).attr("placeholder")),void 0===$(this).attr("placeholder")&&void 0!==$(this).attr("lay-reqtext")&&$(this).attr("placeholder",$(this).attr("layreqtext"))}}),$("div[lay-verify]").removeAttr("lay-verify"))},verifys:function(formObj){layui.form.verify({user:function(value){return new RegExp("^[a-zA-Z0-9_一-龥\\s·]+$").test(value)?/(^\_)|(\__)|(\_+$)/.test(value)?"用户名首尾不能出现下划线'_'":/^\d+\d+\d$/.test(value)?"用户名不能全为数字":void 0:"用户名不能有特殊字符"},pass:[/^[\S]{6,18}$/,"密码必须6到18位,且不能出现空格"],zipcode:[/^\d{6}$/,"请检查邮政编码格式"],chinese:[/^[\u0391-\uFFE5]+$/,"请填写中文字符"],money:[/(^[1-9]([0-9]+)?(\.[0-9]{1,2})?$)|(^(0){1}$)|(^[0-9]\.[0-9]([0-9])?$)/,"请输入正确的金额,且最多两位小数!"],letters:[/^[a-z]+$/i,"请填写字母"],digits:[/^\d+$/,"请填入数字"],qq:[/^[1-9]\d{4,}$/,"请填写有效的QQ号"]})},initForm:function(data){data?(Fun.api.removeEmptyData(data),layui.form.val("form",data)):window.FormArray&&(window.FormArray=Fun.api.removeEmptyData(window.FormArray),layui.form.val("form",window.FormArray)),layui.form.render()},filedelete:function(othis){var that,fileurl=othis.data("fileurl"),confirm=Fun.toastr.confirm(__("Are you sure?"),function(){that=othis.parents(".layui-upload-list").parents(".layui-upload");var input_temp,input=that.find('input[type="text"]'),inputVal=input.val();0===othis.parents("li").index()?(input_temp=inputVal.replace(fileurl,""),input.val(input_temp.replace(/^,|,$/g,""))):(input_temp=inputVal.replace(","+fileurl,""),input.val(input_temp.replace(/^,|,$/g,""))),othis.parents("li").remove(),Fun.toastr.close(confirm)});return!1},autocomplete:function(formObj){var list=void 0!==formObj?formObj.find("*[lay-filter='autoComplete']"):$("*[lay-filter='autoComplete']");list.length>0&&require(["autoComplete"],function(autoComplete){layui.each(list,function(i,v){var _t=$(this),data=_t.data("data"),src=_t.data("src")||_t.data("url"),id=_t.attr("id")||_t.data("id"),keys=_t.data("keys")||null;keys&&(keys=[keys]),0==data.length&&(data=Fun.api.getData(src,{})),window["autoComplete-"+id]=new autoComplete({selector:"#"+id,placeHolder:"Search...",data:{src:data,keys:keys,cache:!0},resultsList:{element:function(list,data){if(!data.results.length){var message=document.createElement("div");message.setAttribute("class","autocomplete_no_result"),message.innerHTML='Found No Results for "'+data.query+'"',list.prepend(message)}},noResults:!0},resultItem:{highlight:!0},events:{input:{selection:function(event){const selection=event.detail.selection.value;window["autoComplete-"+id].input.value=keys?selection[keys]:selection}}}})})})},selects:function(formObj){var list=void 0!==formObj?formObj.find("*[lay-filter='selects']"):$("*[lay-filter='selects']");list.length>0&&require(["selects"],function(selects){selects=layui.selects||parent.layui.selects,layui.each(list,function(i){var _t=$(this),id=_t.prop("id"),url=(_t.attr("name"),_t.data("url")||_t.data("request")),data=_t.data("data")||[],values=_t.data("value")?$(this).data("value"):"",attr=_t.data("attr"),attr=attr&&"string"==typeof attr?attr.split(","):["id","title"];attrs={},layui.isArray(attr)&&(attrs.id=attr.shift(),attrs.title=attr.shift(),attrs.selected=attr[2]?attr[2]:"selected");var opt={elem:this,keywordPlaceholder:"请输入关键词",unfilteredText:"没有匹配的选项",valueSeparator:",",options:[],allowCreate:!0,collapseSelected:!1,url:void 0,parseOptions:void 0};if(attrs&&(opt.customName=attrs),data){datas=[];for(k in data)data[k].hasOwnProperty("id")||(datas[i]={id:k,title:data[k]});data=datas.length>0?datas:data,opt.options=data}url&&(opt.url=Fun.url(url),opt.parseOptions=function(res){return res.data}),window["selects-"+id]=selects.render(opt),window["selects-"+id].val(String(values))})})},selectcx:function(formObj){var list=void 0!==formObj?formObj.find("*[lay-filter='cxselect']"):$("*[lay-filter='cxselect']");list.length>0&&require(["cxSelect"],function(cxSelect){layui.each(list,function(i,v){var _t=$(this),selects=_t.data("selects"),url=_t.data("url"),attr=_t.data("attr")||["id","name"];attr=layui.isArray(attr)?attr:attr.split(","),$.cxSelect.defaults.jsonValue=attr[0]||"id",$.cxSelect.defaults.jsonName=attr?attr[1]:"name",$.cxSelect.defaults.jsonSpace=_t.data("url")?"data":"",_t.cxSelect({url:url,selects:selects,emptyStyle:"none"})})})},selectpage:function(formObj){var list=void 0!==formObj?formObj.find("*[lay-filter='selectPage']"):$("*[lay-filter='selectPage']");list.length>0&&require(["selectPage"],function(selectPage){selectPage=layui.selectPage||parent.layui.selectPage,layui.each(list,function(i){var _t=$(this);value=_t.val()||_t.data("init");var id=_t.prop("id"),name=_t.attr("name")||"id",verify=_t.data("verify")||_t.attr("verify"),url=_t.data("url")||_t.data("request"),isTree=_t.data("istree"),isHtml=_t.data("ishtml"),data=_t.data("data"),field=_t.data("field")||"title",pageSize=_t.data("pagesize")||12,primaryKey=_t.data("primarkey")||"id",selectOnly=_t.data("selectonly")||!1,pagination=!("false"==_t.data("pagination")||0==_t.data("pagination")),listSize=_t.data("listsize")||"15",multiple=_t.data("multiple")||!1,dropButton=_t.data("dropbutton")||!0,maxSelectLimit=_t.data("maxselectlimit ")||0,searchField=_t.data("searchfield")||field,searchKey=_t.data("searchkey")||primaryKey,orderBy=_t.data("orderby")||!1,method=_t.data("method")||"GET",dbTable=_t.data("dbtable"),selectToCloseList=_t.data("selecttocloselist")||!0,disabled=_t.data("disabled")||!1,andOr=_t.data("andor"),formatItem=_t.data("formatitem")||!1,verify=_t.attr("lay-verify")||"";orderBy="string"==layui.type(orderBy)?[orderBy]:orderBy,eSelect=_t.data("eselect"),!value&&window.FormArray&&window.FormArray[name]&&_t.val(window.FormArray[name]);var options={showField:field,keyField:primaryKey,pageSize:pageSize,selectFields:searchField,searchKey:searchKey,isTree:isTree,isHtml:isHtml,data:data||Fun.url(url),dbTable:dbTable,andOr:andOr,method:method,selectOnly:selectOnly,verify:verify,selectToCloseList:selectToCloseList,pagination:pagination,maxSelectLimit:maxSelectLimit,orderBy:orderBy,listSize:listSize,multiple:multiple,dropButton:dropButton,formatItem:function(res){return formatItem?eval(formatItem):res[this.showField]},eSelect:eSelect?eval(eSelect)(res):function(res){},eAjaxSuccess:function(res){return row=res.data,data={},data.list=void 0!==row.data?row.data:[],data.totalRow=void 0!==row.count?row.count:row.data.length,data}};window["selectpage-"+id]=_t.selectPage(options),disabled&&_t.selectPageDisabled(!0)})})},xmselect:function(formObj){var list=void 0!==formObj?formObj.find("*[lay-filter='xmSelect']"):$("*[lay-filter='xmSelect']")||$("*[lay-filter='xmselect']");list.length>0&&require(["xmSelect"],function(xmSelect){layui.each(list,function(i){var _t=$(this),id=_t.prop("id"),url=_t.data("url")||_t.data("request"),lang=_t.data("lang"),value=_t.data("value")||_t.attr("value"),data=_t.data("data")||[],parentfield=_t.data("parentfield")||"pid",tips=_t.data("tips")||"请选择",searchTips=_t.data("searchtips")||"请选择",empty=_t.data("empty")||"呀,没有数据",height=_t.data("height")||"auto",paging=_t.data("paging"),pageSize=_t.data("pagesize"),remoteMethod=_t.data("remotemethod"),content=_t.data("content")||"",radio=_t.data("radio"),disabled=_t.data("disabled"),autoRow=!1!==_t.data("autorow"),clickClose=_t.data("clickclose"),prop=_t.data("prop")||_t.data("attr"),max=_t.data("max"),create=_t.data("create"),on=$(this).data("on"),repeat=!!_t.data("repeat"),theme=_t.data("theme")||"#4d70ff",name=_t.attr("name")||_t.data("name")||"pid",style=_t.data("style")||{},cascader=!!_t.data("cascader")&&{show:!0,indent:200,strict:!1},layVerify=_t.attr("lay-verify")||_t.data("layverify")||"",layReqText=_t.attr("lay-reqtext")||_t.data("layreqtext")||"";layVerType=_t.attr("lay-vertype")||_t.data("layvertype")||"tips";var size=_t.data("size")||"medium";toolbar=0==_t.data("toolbar")?{show:!1}:{show:!0,list:["ALL","CLEAR","REVERSE"]};var filterable=!(void 0!==$(this).data("filterable")&&!_t.data("filterable")),remoteSearch=!(void 0===$(this).data("remotesearch")||!_t.data("remotesearch")),pageRemote=!!$(this).data("pageremote"),props,propArr,options,tree=_t.data("tree");remoteSearch&&(toolbar.show=!0),filterable=!0,tree="object"==typeof tree?tree:!!tree&&{show:!0,showFolderIcon:!0,showLine:!0,indent:20,expandedKeys:[],strict:!1,simple:!1,clickExpand:!0,clickCheck:!0},"object"!=typeof value&&value&&(value="number"==typeof value?[value]:value.split(",")),props={name:"title",value:"id"},selelectFields={name:"title",value:"id"},prop&&(propArr=prop.split(","),props.name=propArr[0],props.value=propArr[1],selelectFields={name:props.name},selelectFields.value=propArr[1]==props.name?"id":propArr[1]),lang=lang||"zh",paging=void 0===paging||"false"!==paging,pageSize=pageSize||10,radio=!!radio,disabled=!!disabled,max=max||0,clickClose=clickClose||!1,xmSelect=window.xmSelect?window.xmSelect:parent.window.xmSelect,options={el:this,language:lang,data:data,initValue:value,name:name,prop:props,tips:tips,empty:empty,searchTips:searchTips,disabled:disabled,filterable:filterable,remoteSearch:remoteSearch,remoteMethod:function(val,cb,show){if(remoteMethod&&void 0!==remoteMethod)eval(remoteMethod)(val,cb,show);else{var formatFilter={},formatOp={};formatFilter[props.name]=val,formatOp[props.name]="%*%",Fun.ajax({method:"get",url:Fun.url(url||window.location.href),data:{filter:JSON.stringify(formatFilter),op:JSON.stringify(formatOp),selectFields:selelectFields}},function(res){cb(res.data)},function(res){cb([])})}},paging:paging,pageSize:pageSize,autoRow:autoRow,size:size,repeat:repeat,height:height,max:max,pageRemote:pageRemote,toolbar:toolbar,theme:{color:theme},radio:radio,layVerify:layVerify,clickClose:clickClose,maxMethod:function(val){},on:function(data){return!!eval(on)&&eval(on)(data)},create:function(val,arr){return eval(create)?eval(create)(val,arr):{name:val,value:val}}},tree&&(options.tree=tree),cascader&&(options.cascader=cascader),style&&(options.style=style),layReqText&&(options.layReqText=layReqText),layVerType&&(options.layVerType=layVerType),content&&(options.content=content),window["xmselect-"+id]=xmSelect.render(options),""===data.toString()&&url&&(searchData={selectFields:selelectFields,tree:tree.show,parentField:parentfield},Fun.ajax({method:"GET",url:Fun.url(url||window.location.href),data:searchData},function(res){window["xmselect-"+id].update({data:res.data,autoRow:autoRow})},function(res){console.log(res)}))})})},editor:function(formObj){var list=void 0!==formObj?formObj.find("*[lay-filter='editor']"):$("*[lay-filter='editor']");if(list.length>0){const useDarkMode=window.matchMedia("(prefers-color-scheme: dark)").matches,isSmallScreen=window.matchMedia("(max-width: 1023.5px)").matches;layui.each(list,function(){var _t=$(this),id=_t.prop("id"),data=_t.data(),name=_t.prop("name"),path=_t.data("path");_t.html(window.FormArray[name]);var upload_url=(data.url?data.url:Fun.url(Upload.init.requests.upload_url))+"?editor=tinymce&path="+path;"tinymce"==$(this).data("editor")&&(0==$("body").find('script[src="/static/plugins/tinymce/tinymce.min.js"]').length&&$("body").append($("#i', '', $html); - } - - return $html; - } - - /** - * Takes a string of HTML (fragment or document) and returns the content - * @todo Consider making protected - */ - public function extractBody($html) - { - $matches = array(); - $result = preg_match('|(.*?)]*>(.*)|is', $html, $matches); - if ($result) { - // Make sure it's not in a comment - $comment_start = strrpos($matches[1], ''); - if ($comment_start === false || - ($comment_end !== false && $comment_end > $comment_start)) { - return $matches[2]; - } - } - return $html; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DOMLex.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DOMLex.php deleted file mode 100644 index ca5f25b84..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DOMLex.php +++ /dev/null @@ -1,338 +0,0 @@ -factory = new HTMLPurifier_TokenFactory(); - } - - /** - * @param string $html - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return HTMLPurifier_Token[] - */ - public function tokenizeHTML($html, $config, $context) - { - $html = $this->normalize($html, $config, $context); - - // attempt to armor stray angled brackets that cannot possibly - // form tags and thus are probably being used as emoticons - if ($config->get('Core.AggressivelyFixLt')) { - $char = '[^a-z!\/]'; - $comment = "/|\z)/is"; - $html = preg_replace_callback($comment, array($this, 'callbackArmorCommentEntities'), $html); - do { - $old = $html; - $html = preg_replace("/<($char)/i", '<\\1', $html); - } while ($html !== $old); - $html = preg_replace_callback($comment, array($this, 'callbackUndoCommentSubst'), $html); // fix comments - } - - // preprocess html, essential for UTF-8 - $html = $this->wrapHTML($html, $config, $context); - - $doc = new DOMDocument(); - $doc->encoding = 'UTF-8'; // theoretically, the above has this covered - - $options = 0; - if ($config->get('Core.AllowParseManyTags') && defined('LIBXML_PARSEHUGE')) { - $options |= LIBXML_PARSEHUGE; - } - - set_error_handler(array($this, 'muteErrorHandler')); - // loadHTML() fails on PHP 5.3 when second parameter is given - if ($options) { - $doc->loadHTML($html, $options); - } else { - $doc->loadHTML($html); - } - restore_error_handler(); - - $body = $doc->getElementsByTagName('html')->item(0)-> // - getElementsByTagName('body')->item(0); // - - $div = $body->getElementsByTagName('div')->item(0); //
                - $tokens = array(); - $this->tokenizeDOM($div, $tokens, $config); - // If the div has a sibling, that means we tripped across - // a premature
                tag. So remove the div we parsed, - // and then tokenize the rest of body. We can't tokenize - // the sibling directly as we'll lose the tags in that case. - if ($div->nextSibling) { - $body->removeChild($div); - $this->tokenizeDOM($body, $tokens, $config); - } - return $tokens; - } - - /** - * Iterative function that tokenizes a node, putting it into an accumulator. - * To iterate is human, to recurse divine - L. Peter Deutsch - * @param DOMNode $node DOMNode to be tokenized. - * @param HTMLPurifier_Token[] $tokens Array-list of already tokenized tokens. - * @return HTMLPurifier_Token of node appended to previously passed tokens. - */ - protected function tokenizeDOM($node, &$tokens, $config) - { - $level = 0; - $nodes = array($level => new HTMLPurifier_Queue(array($node))); - $closingNodes = array(); - do { - while (!$nodes[$level]->isEmpty()) { - $node = $nodes[$level]->shift(); // FIFO - $collect = $level > 0 ? true : false; - $needEndingTag = $this->createStartNode($node, $tokens, $collect, $config); - if ($needEndingTag) { - $closingNodes[$level][] = $node; - } - if ($node->childNodes && $node->childNodes->length) { - $level++; - $nodes[$level] = new HTMLPurifier_Queue(); - foreach ($node->childNodes as $childNode) { - $nodes[$level]->push($childNode); - } - } - } - $level--; - if ($level && isset($closingNodes[$level])) { - while ($node = array_pop($closingNodes[$level])) { - $this->createEndNode($node, $tokens); - } - } - } while ($level > 0); - } - - /** - * Portably retrieve the tag name of a node; deals with older versions - * of libxml like 2.7.6 - * @param DOMNode $node - */ - protected function getTagName($node) - { - if (isset($node->tagName)) { - return $node->tagName; - } else if (isset($node->nodeName)) { - return $node->nodeName; - } else if (isset($node->localName)) { - return $node->localName; - } - return null; - } - - /** - * Portably retrieve the data of a node; deals with older versions - * of libxml like 2.7.6 - * @param DOMNode $node - */ - protected function getData($node) - { - if (isset($node->data)) { - return $node->data; - } else if (isset($node->nodeValue)) { - return $node->nodeValue; - } else if (isset($node->textContent)) { - return $node->textContent; - } - return null; - } - - - /** - * @param DOMNode $node DOMNode to be tokenized. - * @param HTMLPurifier_Token[] $tokens Array-list of already tokenized tokens. - * @param bool $collect Says whether or start and close are collected, set to - * false at first recursion because it's the implicit DIV - * tag you're dealing with. - * @return bool if the token needs an endtoken - * @todo data and tagName properties don't seem to exist in DOMNode? - */ - protected function createStartNode($node, &$tokens, $collect, $config) - { - // intercept non element nodes. WE MUST catch all of them, - // but we're not getting the character reference nodes because - // those should have been preprocessed - if ($node->nodeType === XML_TEXT_NODE) { - $data = $this->getData($node); // Handle variable data property - if ($data !== null) { - $tokens[] = $this->factory->createText($data); - } - return false; - } elseif ($node->nodeType === XML_CDATA_SECTION_NODE) { - // undo libxml's special treatment of )#si', - array($this, 'scriptCallback'), - $html - ); - } - - $html = $this->normalize($html, $config, $context); - - $cursor = 0; // our location in the text - $inside_tag = false; // whether or not we're parsing the inside of a tag - $array = array(); // result array - - // This is also treated to mean maintain *column* numbers too - $maintain_line_numbers = $config->get('Core.MaintainLineNumbers'); - - if ($maintain_line_numbers === null) { - // automatically determine line numbering by checking - // if error collection is on - $maintain_line_numbers = $config->get('Core.CollectErrors'); - } - - if ($maintain_line_numbers) { - $current_line = 1; - $current_col = 0; - $length = strlen($html); - } else { - $current_line = false; - $current_col = false; - $length = false; - } - $context->register('CurrentLine', $current_line); - $context->register('CurrentCol', $current_col); - $nl = "\n"; - // how often to manually recalculate. This will ALWAYS be right, - // but it's pretty wasteful. Set to 0 to turn off - $synchronize_interval = $config->get('Core.DirectLexLineNumberSyncInterval'); - - $e = false; - if ($config->get('Core.CollectErrors')) { - $e =& $context->get('ErrorCollector'); - } - - // for testing synchronization - $loops = 0; - - while (++$loops) { - // $cursor is either at the start of a token, or inside of - // a tag (i.e. there was a < immediately before it), as indicated - // by $inside_tag - - if ($maintain_line_numbers) { - // $rcursor, however, is always at the start of a token. - $rcursor = $cursor - (int)$inside_tag; - - // Column number is cheap, so we calculate it every round. - // We're interested at the *end* of the newline string, so - // we need to add strlen($nl) == 1 to $nl_pos before subtracting it - // from our "rcursor" position. - $nl_pos = strrpos($html, $nl, $rcursor - $length); - $current_col = $rcursor - (is_bool($nl_pos) ? 0 : $nl_pos + 1); - - // recalculate lines - if ($synchronize_interval && // synchronization is on - $cursor > 0 && // cursor is further than zero - $loops % $synchronize_interval === 0) { // time to synchronize! - $current_line = 1 + $this->substrCount($html, $nl, 0, $cursor); - } - } - - $position_next_lt = strpos($html, '<', $cursor); - $position_next_gt = strpos($html, '>', $cursor); - - // triggers on "asdf" but not "asdf " - // special case to set up context - if ($position_next_lt === $cursor) { - $inside_tag = true; - $cursor++; - } - - if (!$inside_tag && $position_next_lt !== false) { - // We are not inside tag and there still is another tag to parse - $token = new - HTMLPurifier_Token_Text( - $this->parseText( - substr( - $html, - $cursor, - $position_next_lt - $cursor - ), $config - ) - ); - if ($maintain_line_numbers) { - $token->rawPosition($current_line, $current_col); - $current_line += $this->substrCount($html, $nl, $cursor, $position_next_lt - $cursor); - } - $array[] = $token; - $cursor = $position_next_lt + 1; - $inside_tag = true; - continue; - } elseif (!$inside_tag) { - // We are not inside tag but there are no more tags - // If we're already at the end, break - if ($cursor === strlen($html)) { - break; - } - // Create Text of rest of string - $token = new - HTMLPurifier_Token_Text( - $this->parseText( - substr( - $html, - $cursor - ), $config - ) - ); - if ($maintain_line_numbers) { - $token->rawPosition($current_line, $current_col); - } - $array[] = $token; - break; - } elseif ($inside_tag && $position_next_gt !== false) { - // We are in tag and it is well formed - // Grab the internals of the tag - $strlen_segment = $position_next_gt - $cursor; - - if ($strlen_segment < 1) { - // there's nothing to process! - $token = new HTMLPurifier_Token_Text('<'); - $cursor++; - continue; - } - - $segment = substr($html, $cursor, $strlen_segment); - - if ($segment === false) { - // somehow, we attempted to access beyond the end of - // the string, defense-in-depth, reported by Nate Abele - break; - } - - // Check if it's a comment - if (substr($segment, 0, 3) === '!--') { - // re-determine segment length, looking for --> - $position_comment_end = strpos($html, '-->', $cursor); - if ($position_comment_end === false) { - // uh oh, we have a comment that extends to - // infinity. Can't be helped: set comment - // end position to end of string - if ($e) { - $e->send(E_WARNING, 'Lexer: Unclosed comment'); - } - $position_comment_end = strlen($html); - $end = true; - } else { - $end = false; - } - $strlen_segment = $position_comment_end - $cursor; - $segment = substr($html, $cursor, $strlen_segment); - $token = new - HTMLPurifier_Token_Comment( - substr( - $segment, - 3, - $strlen_segment - 3 - ) - ); - if ($maintain_line_numbers) { - $token->rawPosition($current_line, $current_col); - $current_line += $this->substrCount($html, $nl, $cursor, $strlen_segment); - } - $array[] = $token; - $cursor = $end ? $position_comment_end : $position_comment_end + 3; - $inside_tag = false; - continue; - } - - // Check if it's an end tag - $is_end_tag = (strpos($segment, '/') === 0); - if ($is_end_tag) { - $type = substr($segment, 1); - $token = new HTMLPurifier_Token_End($type); - if ($maintain_line_numbers) { - $token->rawPosition($current_line, $current_col); - $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor); - } - $array[] = $token; - $inside_tag = false; - $cursor = $position_next_gt + 1; - continue; - } - - // Check leading character is alnum, if not, we may - // have accidently grabbed an emoticon. Translate into - // text and go our merry way - if (!ctype_alpha($segment[0])) { - // XML: $segment[0] !== '_' && $segment[0] !== ':' - if ($e) { - $e->send(E_NOTICE, 'Lexer: Unescaped lt'); - } - $token = new HTMLPurifier_Token_Text('<'); - if ($maintain_line_numbers) { - $token->rawPosition($current_line, $current_col); - $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor); - } - $array[] = $token; - $inside_tag = false; - continue; - } - - // Check if it is explicitly self closing, if so, remove - // trailing slash. Remember, we could have a tag like
                , so - // any later token processing scripts must convert improperly - // classified EmptyTags from StartTags. - $is_self_closing = (strrpos($segment, '/') === $strlen_segment - 1); - if ($is_self_closing) { - $strlen_segment--; - $segment = substr($segment, 0, $strlen_segment); - } - - // Check if there are any attributes - $position_first_space = strcspn($segment, $this->_whitespace); - - if ($position_first_space >= $strlen_segment) { - if ($is_self_closing) { - $token = new HTMLPurifier_Token_Empty($segment); - } else { - $token = new HTMLPurifier_Token_Start($segment); - } - if ($maintain_line_numbers) { - $token->rawPosition($current_line, $current_col); - $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor); - } - $array[] = $token; - $inside_tag = false; - $cursor = $position_next_gt + 1; - continue; - } - - // Grab out all the data - $type = substr($segment, 0, $position_first_space); - $attribute_string = - trim( - substr( - $segment, - $position_first_space - ) - ); - if ($attribute_string) { - $attr = $this->parseAttributeString( - $attribute_string, - $config, - $context - ); - } else { - $attr = array(); - } - - if ($is_self_closing) { - $token = new HTMLPurifier_Token_Empty($type, $attr); - } else { - $token = new HTMLPurifier_Token_Start($type, $attr); - } - if ($maintain_line_numbers) { - $token->rawPosition($current_line, $current_col); - $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor); - } - $array[] = $token; - $cursor = $position_next_gt + 1; - $inside_tag = false; - continue; - } else { - // inside tag, but there's no ending > sign - if ($e) { - $e->send(E_WARNING, 'Lexer: Missing gt'); - } - $token = new - HTMLPurifier_Token_Text( - '<' . - $this->parseText( - substr($html, $cursor), $config - ) - ); - if ($maintain_line_numbers) { - $token->rawPosition($current_line, $current_col); - } - // no cursor scroll? Hmm... - $array[] = $token; - break; - } - break; - } - - $context->destroy('CurrentLine'); - $context->destroy('CurrentCol'); - return $array; - } - - /** - * PHP 5.0.x compatible substr_count that implements offset and length - * @param string $haystack - * @param string $needle - * @param int $offset - * @param int $length - * @return int - */ - protected function substrCount($haystack, $needle, $offset, $length) - { - static $oldVersion; - if ($oldVersion === null) { - $oldVersion = version_compare(PHP_VERSION, '5.1', '<'); - } - if ($oldVersion) { - $haystack = substr($haystack, $offset, $length); - return substr_count($haystack, $needle); - } else { - return substr_count($haystack, $needle, $offset, $length); - } - } - - /** - * Takes the inside of an HTML tag and makes an assoc array of attributes. - * - * @param string $string Inside of tag excluding name. - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return array Assoc array of attributes. - */ - public function parseAttributeString($string, $config, $context) - { - $string = (string)$string; // quick typecast - - if ($string == '') { - return array(); - } // no attributes - - $e = false; - if ($config->get('Core.CollectErrors')) { - $e =& $context->get('ErrorCollector'); - } - - // let's see if we can abort as quickly as possible - // one equal sign, no spaces => one attribute - $num_equal = substr_count($string, '='); - $has_space = strpos($string, ' '); - if ($num_equal === 0 && !$has_space) { - // bool attribute - return array($string => $string); - } elseif ($num_equal === 1 && !$has_space) { - // only one attribute - list($key, $quoted_value) = explode('=', $string); - $quoted_value = trim($quoted_value); - if (!$key) { - if ($e) { - $e->send(E_ERROR, 'Lexer: Missing attribute key'); - } - return array(); - } - if (!$quoted_value) { - return array($key => ''); - } - $first_char = @$quoted_value[0]; - $last_char = @$quoted_value[strlen($quoted_value) - 1]; - - $same_quote = ($first_char == $last_char); - $open_quote = ($first_char == '"' || $first_char == "'"); - - if ($same_quote && $open_quote) { - // well behaved - $value = substr($quoted_value, 1, strlen($quoted_value) - 2); - } else { - // not well behaved - if ($open_quote) { - if ($e) { - $e->send(E_ERROR, 'Lexer: Missing end quote'); - } - $value = substr($quoted_value, 1); - } else { - $value = $quoted_value; - } - } - if ($value === false) { - $value = ''; - } - return array($key => $this->parseAttr($value, $config)); - } - - // setup loop environment - $array = array(); // return assoc array of attributes - $cursor = 0; // current position in string (moves forward) - $size = strlen($string); // size of the string (stays the same) - - // if we have unquoted attributes, the parser expects a terminating - // space, so let's guarantee that there's always a terminating space. - $string .= ' '; - - $old_cursor = -1; - while ($cursor < $size) { - if ($old_cursor >= $cursor) { - throw new Exception("Infinite loop detected"); - } - $old_cursor = $cursor; - - $cursor += ($value = strspn($string, $this->_whitespace, $cursor)); - // grab the key - - $key_begin = $cursor; //we're currently at the start of the key - - // scroll past all characters that are the key (not whitespace or =) - $cursor += strcspn($string, $this->_whitespace . '=', $cursor); - - $key_end = $cursor; // now at the end of the key - - $key = substr($string, $key_begin, $key_end - $key_begin); - - if (!$key) { - if ($e) { - $e->send(E_ERROR, 'Lexer: Missing attribute key'); - } - $cursor += 1 + strcspn($string, $this->_whitespace, $cursor + 1); // prevent infinite loop - continue; // empty key - } - - // scroll past all whitespace - $cursor += strspn($string, $this->_whitespace, $cursor); - - if ($cursor >= $size) { - $array[$key] = $key; - break; - } - - // if the next character is an equal sign, we've got a regular - // pair, otherwise, it's a bool attribute - $first_char = @$string[$cursor]; - - if ($first_char == '=') { - // key="value" - - $cursor++; - $cursor += strspn($string, $this->_whitespace, $cursor); - - if ($cursor === false) { - $array[$key] = ''; - break; - } - - // we might be in front of a quote right now - - $char = @$string[$cursor]; - - if ($char == '"' || $char == "'") { - // it's quoted, end bound is $char - $cursor++; - $value_begin = $cursor; - $cursor = strpos($string, $char, $cursor); - $value_end = $cursor; - } else { - // it's not quoted, end bound is whitespace - $value_begin = $cursor; - $cursor += strcspn($string, $this->_whitespace, $cursor); - $value_end = $cursor; - } - - // we reached a premature end - if ($cursor === false) { - $cursor = $size; - $value_end = $cursor; - } - - $value = substr($string, $value_begin, $value_end - $value_begin); - if ($value === false) { - $value = ''; - } - $array[$key] = $this->parseAttr($value, $config); - $cursor++; - } else { - // boolattr - if ($key !== '') { - $array[$key] = $key; - } else { - // purely theoretical - if ($e) { - $e->send(E_ERROR, 'Lexer: Missing attribute key'); - } - } - } - } - return $array; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/PH5P.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/PH5P.php deleted file mode 100644 index 1564f283d..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/PH5P.php +++ /dev/null @@ -1,4788 +0,0 @@ -normalize($html, $config, $context); - $new_html = $this->wrapHTML($new_html, $config, $context, false /* no div */); - try { - $parser = new HTML5($new_html); - $doc = $parser->save(); - } catch (DOMException $e) { - // Uh oh, it failed. Punt to DirectLex. - $lexer = new HTMLPurifier_Lexer_DirectLex(); - $context->register('PH5PError', $e); // save the error, so we can detect it - return $lexer->tokenizeHTML($html, $config, $context); // use original HTML - } - $tokens = array(); - $this->tokenizeDOM( - $doc->getElementsByTagName('html')->item(0)-> // - getElementsByTagName('body')->item(0) // - , - $tokens, $config - ); - return $tokens; - } -} - -/* - -Copyright 2007 Jeroen van der Meer - -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. - -*/ - -class HTML5 -{ - private $data; - private $char; - private $EOF; - private $state; - private $tree; - private $token; - private $content_model; - private $escape = false; - private $entities = array( - 'AElig;', - 'AElig', - 'AMP;', - 'AMP', - 'Aacute;', - 'Aacute', - 'Acirc;', - 'Acirc', - 'Agrave;', - 'Agrave', - 'Alpha;', - 'Aring;', - 'Aring', - 'Atilde;', - 'Atilde', - 'Auml;', - 'Auml', - 'Beta;', - 'COPY;', - 'COPY', - 'Ccedil;', - 'Ccedil', - 'Chi;', - 'Dagger;', - 'Delta;', - 'ETH;', - 'ETH', - 'Eacute;', - 'Eacute', - 'Ecirc;', - 'Ecirc', - 'Egrave;', - 'Egrave', - 'Epsilon;', - 'Eta;', - 'Euml;', - 'Euml', - 'GT;', - 'GT', - 'Gamma;', - 'Iacute;', - 'Iacute', - 'Icirc;', - 'Icirc', - 'Igrave;', - 'Igrave', - 'Iota;', - 'Iuml;', - 'Iuml', - 'Kappa;', - 'LT;', - 'LT', - 'Lambda;', - 'Mu;', - 'Ntilde;', - 'Ntilde', - 'Nu;', - 'OElig;', - 'Oacute;', - 'Oacute', - 'Ocirc;', - 'Ocirc', - 'Ograve;', - 'Ograve', - 'Omega;', - 'Omicron;', - 'Oslash;', - 'Oslash', - 'Otilde;', - 'Otilde', - 'Ouml;', - 'Ouml', - 'Phi;', - 'Pi;', - 'Prime;', - 'Psi;', - 'QUOT;', - 'QUOT', - 'REG;', - 'REG', - 'Rho;', - 'Scaron;', - 'Sigma;', - 'THORN;', - 'THORN', - 'TRADE;', - 'Tau;', - 'Theta;', - 'Uacute;', - 'Uacute', - 'Ucirc;', - 'Ucirc', - 'Ugrave;', - 'Ugrave', - 'Upsilon;', - 'Uuml;', - 'Uuml', - 'Xi;', - 'Yacute;', - 'Yacute', - 'Yuml;', - 'Zeta;', - 'aacute;', - 'aacute', - 'acirc;', - 'acirc', - 'acute;', - 'acute', - 'aelig;', - 'aelig', - 'agrave;', - 'agrave', - 'alefsym;', - 'alpha;', - 'amp;', - 'amp', - 'and;', - 'ang;', - 'apos;', - 'aring;', - 'aring', - 'asymp;', - 'atilde;', - 'atilde', - 'auml;', - 'auml', - 'bdquo;', - 'beta;', - 'brvbar;', - 'brvbar', - 'bull;', - 'cap;', - 'ccedil;', - 'ccedil', - 'cedil;', - 'cedil', - 'cent;', - 'cent', - 'chi;', - 'circ;', - 'clubs;', - 'cong;', - 'copy;', - 'copy', - 'crarr;', - 'cup;', - 'curren;', - 'curren', - 'dArr;', - 'dagger;', - 'darr;', - 'deg;', - 'deg', - 'delta;', - 'diams;', - 'divide;', - 'divide', - 'eacute;', - 'eacute', - 'ecirc;', - 'ecirc', - 'egrave;', - 'egrave', - 'empty;', - 'emsp;', - 'ensp;', - 'epsilon;', - 'equiv;', - 'eta;', - 'eth;', - 'eth', - 'euml;', - 'euml', - 'euro;', - 'exist;', - 'fnof;', - 'forall;', - 'frac12;', - 'frac12', - 'frac14;', - 'frac14', - 'frac34;', - 'frac34', - 'frasl;', - 'gamma;', - 'ge;', - 'gt;', - 'gt', - 'hArr;', - 'harr;', - 'hearts;', - 'hellip;', - 'iacute;', - 'iacute', - 'icirc;', - 'icirc', - 'iexcl;', - 'iexcl', - 'igrave;', - 'igrave', - 'image;', - 'infin;', - 'int;', - 'iota;', - 'iquest;', - 'iquest', - 'isin;', - 'iuml;', - 'iuml', - 'kappa;', - 'lArr;', - 'lambda;', - 'lang;', - 'laquo;', - 'laquo', - 'larr;', - 'lceil;', - 'ldquo;', - 'le;', - 'lfloor;', - 'lowast;', - 'loz;', - 'lrm;', - 'lsaquo;', - 'lsquo;', - 'lt;', - 'lt', - 'macr;', - 'macr', - 'mdash;', - 'micro;', - 'micro', - 'middot;', - 'middot', - 'minus;', - 'mu;', - 'nabla;', - 'nbsp;', - 'nbsp', - 'ndash;', - 'ne;', - 'ni;', - 'not;', - 'not', - 'notin;', - 'nsub;', - 'ntilde;', - 'ntilde', - 'nu;', - 'oacute;', - 'oacute', - 'ocirc;', - 'ocirc', - 'oelig;', - 'ograve;', - 'ograve', - 'oline;', - 'omega;', - 'omicron;', - 'oplus;', - 'or;', - 'ordf;', - 'ordf', - 'ordm;', - 'ordm', - 'oslash;', - 'oslash', - 'otilde;', - 'otilde', - 'otimes;', - 'ouml;', - 'ouml', - 'para;', - 'para', - 'part;', - 'permil;', - 'perp;', - 'phi;', - 'pi;', - 'piv;', - 'plusmn;', - 'plusmn', - 'pound;', - 'pound', - 'prime;', - 'prod;', - 'prop;', - 'psi;', - 'quot;', - 'quot', - 'rArr;', - 'radic;', - 'rang;', - 'raquo;', - 'raquo', - 'rarr;', - 'rceil;', - 'rdquo;', - 'real;', - 'reg;', - 'reg', - 'rfloor;', - 'rho;', - 'rlm;', - 'rsaquo;', - 'rsquo;', - 'sbquo;', - 'scaron;', - 'sdot;', - 'sect;', - 'sect', - 'shy;', - 'shy', - 'sigma;', - 'sigmaf;', - 'sim;', - 'spades;', - 'sub;', - 'sube;', - 'sum;', - 'sup1;', - 'sup1', - 'sup2;', - 'sup2', - 'sup3;', - 'sup3', - 'sup;', - 'supe;', - 'szlig;', - 'szlig', - 'tau;', - 'there4;', - 'theta;', - 'thetasym;', - 'thinsp;', - 'thorn;', - 'thorn', - 'tilde;', - 'times;', - 'times', - 'trade;', - 'uArr;', - 'uacute;', - 'uacute', - 'uarr;', - 'ucirc;', - 'ucirc', - 'ugrave;', - 'ugrave', - 'uml;', - 'uml', - 'upsih;', - 'upsilon;', - 'uuml;', - 'uuml', - 'weierp;', - 'xi;', - 'yacute;', - 'yacute', - 'yen;', - 'yen', - 'yuml;', - 'yuml', - 'zeta;', - 'zwj;', - 'zwnj;' - ); - - const PCDATA = 0; - const RCDATA = 1; - const CDATA = 2; - const PLAINTEXT = 3; - - const DOCTYPE = 0; - const STARTTAG = 1; - const ENDTAG = 2; - const COMMENT = 3; - const CHARACTR = 4; - const EOF = 5; - - public function __construct($data) - { - $this->data = $data; - $this->char = -1; - $this->EOF = strlen($data); - $this->tree = new HTML5TreeConstructer; - $this->content_model = self::PCDATA; - - $this->state = 'data'; - - while ($this->state !== null) { - $this->{$this->state . 'State'}(); - } - } - - public function save() - { - return $this->tree->save(); - } - - private function char() - { - return ($this->char < $this->EOF) - ? $this->data[$this->char] - : false; - } - - private function character($s, $l = 0) - { - if ($s + $l < $this->EOF) { - if ($l === 0) { - return $this->data[$s]; - } else { - return substr($this->data, $s, $l); - } - } - } - - private function characters($char_class, $start) - { - return preg_replace('#^([' . $char_class . ']+).*#s', '\\1', substr($this->data, $start)); - } - - private function dataState() - { - // Consume the next input character - $this->char++; - $char = $this->char(); - - if ($char === '&' && ($this->content_model === self::PCDATA || $this->content_model === self::RCDATA)) { - /* U+0026 AMPERSAND (&) - When the content model flag is set to one of the PCDATA or RCDATA - states: switch to the entity data state. Otherwise: treat it as per - the "anything else" entry below. */ - $this->state = 'entityData'; - - } elseif ($char === '-') { - /* If the content model flag is set to either the RCDATA state or - the CDATA state, and the escape flag is false, and there are at - least three characters before this one in the input stream, and the - last four characters in the input stream, including this one, are - U+003C LESS-THAN SIGN, U+0021 EXCLAMATION MARK, U+002D HYPHEN-MINUS, - and U+002D HYPHEN-MINUS (""), - set the escape flag to false. */ - if (($this->content_model === self::RCDATA || - $this->content_model === self::CDATA) && $this->escape === true && - $this->character($this->char, 3) === '-->' - ) { - $this->escape = false; - } - - /* In any case, emit the input character as a character token. - Stay in the data state. */ - $this->emitToken( - array( - 'type' => self::CHARACTR, - 'data' => $char - ) - ); - - } elseif ($this->char === $this->EOF) { - /* EOF - Emit an end-of-file token. */ - $this->EOF(); - - } elseif ($this->content_model === self::PLAINTEXT) { - /* When the content model flag is set to the PLAINTEXT state - THIS DIFFERS GREATLY FROM THE SPEC: Get the remaining characters of - the text and emit it as a character token. */ - $this->emitToken( - array( - 'type' => self::CHARACTR, - 'data' => substr($this->data, $this->char) - ) - ); - - $this->EOF(); - - } else { - /* Anything else - THIS DIFFERS GREATLY FROM THE SPEC: Get as many character that - otherwise would also be treated as a character token and emit it - as a single character token. Stay in the data state. */ - $len = strcspn($this->data, '<&', $this->char); - $char = substr($this->data, $this->char, $len); - $this->char += $len - 1; - - $this->emitToken( - array( - 'type' => self::CHARACTR, - 'data' => $char - ) - ); - - $this->state = 'data'; - } - } - - private function entityDataState() - { - // Attempt to consume an entity. - $entity = $this->entity(); - - // If nothing is returned, emit a U+0026 AMPERSAND character token. - // Otherwise, emit the character token that was returned. - $char = (!$entity) ? '&' : $entity; - $this->emitToken( - array( - 'type' => self::CHARACTR, - 'data' => $char - ) - ); - - // Finally, switch to the data state. - $this->state = 'data'; - } - - private function tagOpenState() - { - switch ($this->content_model) { - case self::RCDATA: - case self::CDATA: - /* If the next input character is a U+002F SOLIDUS (/) character, - consume it and switch to the close tag open state. If the next - input character is not a U+002F SOLIDUS (/) character, emit a - U+003C LESS-THAN SIGN character token and switch to the data - state to process the next input character. */ - if ($this->character($this->char + 1) === '/') { - $this->char++; - $this->state = 'closeTagOpen'; - - } else { - $this->emitToken( - array( - 'type' => self::CHARACTR, - 'data' => '<' - ) - ); - - $this->state = 'data'; - } - break; - - case self::PCDATA: - // If the content model flag is set to the PCDATA state - // Consume the next input character: - $this->char++; - $char = $this->char(); - - if ($char === '!') { - /* U+0021 EXCLAMATION MARK (!) - Switch to the markup declaration open state. */ - $this->state = 'markupDeclarationOpen'; - - } elseif ($char === '/') { - /* U+002F SOLIDUS (/) - Switch to the close tag open state. */ - $this->state = 'closeTagOpen'; - - } elseif (preg_match('/^[A-Za-z]$/', $char)) { - /* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z - Create a new start tag token, set its tag name to the lowercase - version of the input character (add 0x0020 to the character's code - point), then switch to the tag name state. (Don't emit the token - yet; further details will be filled in before it is emitted.) */ - $this->token = array( - 'name' => strtolower($char), - 'type' => self::STARTTAG, - 'attr' => array() - ); - - $this->state = 'tagName'; - - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Parse error. Emit a U+003C LESS-THAN SIGN character token and a - U+003E GREATER-THAN SIGN character token. Switch to the data state. */ - $this->emitToken( - array( - 'type' => self::CHARACTR, - 'data' => '<>' - ) - ); - - $this->state = 'data'; - - } elseif ($char === '?') { - /* U+003F QUESTION MARK (?) - Parse error. Switch to the bogus comment state. */ - $this->state = 'bogusComment'; - - } else { - /* Anything else - Parse error. Emit a U+003C LESS-THAN SIGN character token and - reconsume the current input character in the data state. */ - $this->emitToken( - array( - 'type' => self::CHARACTR, - 'data' => '<' - ) - ); - - $this->char--; - $this->state = 'data'; - } - break; - } - } - - private function closeTagOpenState() - { - $next_node = strtolower($this->characters('A-Za-z', $this->char + 1)); - $the_same = count($this->tree->stack) > 0 && $next_node === end($this->tree->stack)->nodeName; - - if (($this->content_model === self::RCDATA || $this->content_model === self::CDATA) && - (!$the_same || ($the_same && (!preg_match( - '/[\t\n\x0b\x0c >\/]/', - $this->character($this->char + 1 + strlen($next_node)) - ) || $this->EOF === $this->char))) - ) { - /* If the content model flag is set to the RCDATA or CDATA states then - examine the next few characters. If they do not match the tag name of - the last start tag token emitted (case insensitively), or if they do but - they are not immediately followed by one of the following characters: - * U+0009 CHARACTER TABULATION - * U+000A LINE FEED (LF) - * U+000B LINE TABULATION - * U+000C FORM FEED (FF) - * U+0020 SPACE - * U+003E GREATER-THAN SIGN (>) - * U+002F SOLIDUS (/) - * EOF - ...then there is a parse error. Emit a U+003C LESS-THAN SIGN character - token, a U+002F SOLIDUS character token, and switch to the data state - to process the next input character. */ - $this->emitToken( - array( - 'type' => self::CHARACTR, - 'data' => 'state = 'data'; - - } else { - /* Otherwise, if the content model flag is set to the PCDATA state, - or if the next few characters do match that tag name, consume the - next input character: */ - $this->char++; - $char = $this->char(); - - if (preg_match('/^[A-Za-z]$/', $char)) { - /* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z - Create a new end tag token, set its tag name to the lowercase version - of the input character (add 0x0020 to the character's code point), then - switch to the tag name state. (Don't emit the token yet; further details - will be filled in before it is emitted.) */ - $this->token = array( - 'name' => strtolower($char), - 'type' => self::ENDTAG - ); - - $this->state = 'tagName'; - - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Parse error. Switch to the data state. */ - $this->state = 'data'; - - } elseif ($this->char === $this->EOF) { - /* EOF - Parse error. Emit a U+003C LESS-THAN SIGN character token and a U+002F - SOLIDUS character token. Reconsume the EOF character in the data state. */ - $this->emitToken( - array( - 'type' => self::CHARACTR, - 'data' => 'char--; - $this->state = 'data'; - - } else { - /* Parse error. Switch to the bogus comment state. */ - $this->state = 'bogusComment'; - } - } - } - - private function tagNameState() - { - // Consume the next input character: - $this->char++; - $char = $this->character($this->char); - - if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000B LINE TABULATION - U+000C FORM FEED (FF) - U+0020 SPACE - Switch to the before attribute name state. */ - $this->state = 'beforeAttributeName'; - - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current tag token. Switch to the data state. */ - $this->emitToken($this->token); - $this->state = 'data'; - - } elseif ($this->char === $this->EOF) { - /* EOF - Parse error. Emit the current tag token. Reconsume the EOF - character in the data state. */ - $this->emitToken($this->token); - - $this->char--; - $this->state = 'data'; - - } elseif ($char === '/') { - /* U+002F SOLIDUS (/) - Parse error unless this is a permitted slash. Switch to the before - attribute name state. */ - $this->state = 'beforeAttributeName'; - - } else { - /* Anything else - Append the current input character to the current tag token's tag name. - Stay in the tag name state. */ - $this->token['name'] .= strtolower($char); - $this->state = 'tagName'; - } - } - - private function beforeAttributeNameState() - { - // Consume the next input character: - $this->char++; - $char = $this->character($this->char); - - if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000B LINE TABULATION - U+000C FORM FEED (FF) - U+0020 SPACE - Stay in the before attribute name state. */ - $this->state = 'beforeAttributeName'; - - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current tag token. Switch to the data state. */ - $this->emitToken($this->token); - $this->state = 'data'; - - } elseif ($char === '/') { - /* U+002F SOLIDUS (/) - Parse error unless this is a permitted slash. Stay in the before - attribute name state. */ - $this->state = 'beforeAttributeName'; - - } elseif ($this->char === $this->EOF) { - /* EOF - Parse error. Emit the current tag token. Reconsume the EOF - character in the data state. */ - $this->emitToken($this->token); - - $this->char--; - $this->state = 'data'; - - } else { - /* Anything else - Start a new attribute in the current tag token. Set that attribute's - name to the current input character, and its value to the empty string. - Switch to the attribute name state. */ - $this->token['attr'][] = array( - 'name' => strtolower($char), - 'value' => null - ); - - $this->state = 'attributeName'; - } - } - - private function attributeNameState() - { - // Consume the next input character: - $this->char++; - $char = $this->character($this->char); - - if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000B LINE TABULATION - U+000C FORM FEED (FF) - U+0020 SPACE - Stay in the before attribute name state. */ - $this->state = 'afterAttributeName'; - - } elseif ($char === '=') { - /* U+003D EQUALS SIGN (=) - Switch to the before attribute value state. */ - $this->state = 'beforeAttributeValue'; - - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current tag token. Switch to the data state. */ - $this->emitToken($this->token); - $this->state = 'data'; - - } elseif ($char === '/' && $this->character($this->char + 1) !== '>') { - /* U+002F SOLIDUS (/) - Parse error unless this is a permitted slash. Switch to the before - attribute name state. */ - $this->state = 'beforeAttributeName'; - - } elseif ($this->char === $this->EOF) { - /* EOF - Parse error. Emit the current tag token. Reconsume the EOF - character in the data state. */ - $this->emitToken($this->token); - - $this->char--; - $this->state = 'data'; - - } else { - /* Anything else - Append the current input character to the current attribute's name. - Stay in the attribute name state. */ - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['name'] .= strtolower($char); - - $this->state = 'attributeName'; - } - } - - private function afterAttributeNameState() - { - // Consume the next input character: - $this->char++; - $char = $this->character($this->char); - - if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000B LINE TABULATION - U+000C FORM FEED (FF) - U+0020 SPACE - Stay in the after attribute name state. */ - $this->state = 'afterAttributeName'; - - } elseif ($char === '=') { - /* U+003D EQUALS SIGN (=) - Switch to the before attribute value state. */ - $this->state = 'beforeAttributeValue'; - - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current tag token. Switch to the data state. */ - $this->emitToken($this->token); - $this->state = 'data'; - - } elseif ($char === '/' && $this->character($this->char + 1) !== '>') { - /* U+002F SOLIDUS (/) - Parse error unless this is a permitted slash. Switch to the - before attribute name state. */ - $this->state = 'beforeAttributeName'; - - } elseif ($this->char === $this->EOF) { - /* EOF - Parse error. Emit the current tag token. Reconsume the EOF - character in the data state. */ - $this->emitToken($this->token); - - $this->char--; - $this->state = 'data'; - - } else { - /* Anything else - Start a new attribute in the current tag token. Set that attribute's - name to the current input character, and its value to the empty string. - Switch to the attribute name state. */ - $this->token['attr'][] = array( - 'name' => strtolower($char), - 'value' => null - ); - - $this->state = 'attributeName'; - } - } - - private function beforeAttributeValueState() - { - // Consume the next input character: - $this->char++; - $char = $this->character($this->char); - - if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000B LINE TABULATION - U+000C FORM FEED (FF) - U+0020 SPACE - Stay in the before attribute value state. */ - $this->state = 'beforeAttributeValue'; - - } elseif ($char === '"') { - /* U+0022 QUOTATION MARK (") - Switch to the attribute value (double-quoted) state. */ - $this->state = 'attributeValueDoubleQuoted'; - - } elseif ($char === '&') { - /* U+0026 AMPERSAND (&) - Switch to the attribute value (unquoted) state and reconsume - this input character. */ - $this->char--; - $this->state = 'attributeValueUnquoted'; - - } elseif ($char === '\'') { - /* U+0027 APOSTROPHE (') - Switch to the attribute value (single-quoted) state. */ - $this->state = 'attributeValueSingleQuoted'; - - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current tag token. Switch to the data state. */ - $this->emitToken($this->token); - $this->state = 'data'; - - } else { - /* Anything else - Append the current input character to the current attribute's value. - Switch to the attribute value (unquoted) state. */ - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['value'] .= $char; - - $this->state = 'attributeValueUnquoted'; - } - } - - private function attributeValueDoubleQuotedState() - { - // Consume the next input character: - $this->char++; - $char = $this->character($this->char); - - if ($char === '"') { - /* U+0022 QUOTATION MARK (") - Switch to the before attribute name state. */ - $this->state = 'beforeAttributeName'; - - } elseif ($char === '&') { - /* U+0026 AMPERSAND (&) - Switch to the entity in attribute value state. */ - $this->entityInAttributeValueState('double'); - - } elseif ($this->char === $this->EOF) { - /* EOF - Parse error. Emit the current tag token. Reconsume the character - in the data state. */ - $this->emitToken($this->token); - - $this->char--; - $this->state = 'data'; - - } else { - /* Anything else - Append the current input character to the current attribute's value. - Stay in the attribute value (double-quoted) state. */ - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['value'] .= $char; - - $this->state = 'attributeValueDoubleQuoted'; - } - } - - private function attributeValueSingleQuotedState() - { - // Consume the next input character: - $this->char++; - $char = $this->character($this->char); - - if ($char === '\'') { - /* U+0022 QUOTATION MARK (') - Switch to the before attribute name state. */ - $this->state = 'beforeAttributeName'; - - } elseif ($char === '&') { - /* U+0026 AMPERSAND (&) - Switch to the entity in attribute value state. */ - $this->entityInAttributeValueState('single'); - - } elseif ($this->char === $this->EOF) { - /* EOF - Parse error. Emit the current tag token. Reconsume the character - in the data state. */ - $this->emitToken($this->token); - - $this->char--; - $this->state = 'data'; - - } else { - /* Anything else - Append the current input character to the current attribute's value. - Stay in the attribute value (single-quoted) state. */ - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['value'] .= $char; - - $this->state = 'attributeValueSingleQuoted'; - } - } - - private function attributeValueUnquotedState() - { - // Consume the next input character: - $this->char++; - $char = $this->character($this->char); - - if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000B LINE TABULATION - U+000C FORM FEED (FF) - U+0020 SPACE - Switch to the before attribute name state. */ - $this->state = 'beforeAttributeName'; - - } elseif ($char === '&') { - /* U+0026 AMPERSAND (&) - Switch to the entity in attribute value state. */ - $this->entityInAttributeValueState(); - - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current tag token. Switch to the data state. */ - $this->emitToken($this->token); - $this->state = 'data'; - - } else { - /* Anything else - Append the current input character to the current attribute's value. - Stay in the attribute value (unquoted) state. */ - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['value'] .= $char; - - $this->state = 'attributeValueUnquoted'; - } - } - - private function entityInAttributeValueState() - { - // Attempt to consume an entity. - $entity = $this->entity(); - - // If nothing is returned, append a U+0026 AMPERSAND character to the - // current attribute's value. Otherwise, emit the character token that - // was returned. - $char = (!$entity) - ? '&' - : $entity; - - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['value'] .= $char; - } - - private function bogusCommentState() - { - /* Consume every character up to the first U+003E GREATER-THAN SIGN - character (>) or the end of the file (EOF), whichever comes first. Emit - a comment token whose data is the concatenation of all the characters - starting from and including the character that caused the state machine - to switch into the bogus comment state, up to and including the last - consumed character before the U+003E character, if any, or up to the - end of the file otherwise. (If the comment was started by the end of - the file (EOF), the token is empty.) */ - $data = $this->characters('^>', $this->char); - $this->emitToken( - array( - 'data' => $data, - 'type' => self::COMMENT - ) - ); - - $this->char += strlen($data); - - /* Switch to the data state. */ - $this->state = 'data'; - - /* If the end of the file was reached, reconsume the EOF character. */ - if ($this->char === $this->EOF) { - $this->char = $this->EOF - 1; - } - } - - private function markupDeclarationOpenState() - { - /* If the next two characters are both U+002D HYPHEN-MINUS (-) - characters, consume those two characters, create a comment token whose - data is the empty string, and switch to the comment state. */ - if ($this->character($this->char + 1, 2) === '--') { - $this->char += 2; - $this->state = 'comment'; - $this->token = array( - 'data' => null, - 'type' => self::COMMENT - ); - - /* Otherwise if the next seven chacacters are a case-insensitive match - for the word "DOCTYPE", then consume those characters and switch to the - DOCTYPE state. */ - } elseif (strtolower($this->character($this->char + 1, 7)) === 'doctype') { - $this->char += 7; - $this->state = 'doctype'; - - /* Otherwise, is is a parse error. Switch to the bogus comment state. - The next character that is consumed, if any, is the first character - that will be in the comment. */ - } else { - $this->char++; - $this->state = 'bogusComment'; - } - } - - private function commentState() - { - /* Consume the next input character: */ - $this->char++; - $char = $this->char(); - - /* U+002D HYPHEN-MINUS (-) */ - if ($char === '-') { - /* Switch to the comment dash state */ - $this->state = 'commentDash'; - - /* EOF */ - } elseif ($this->char === $this->EOF) { - /* Parse error. Emit the comment token. Reconsume the EOF character - in the data state. */ - $this->emitToken($this->token); - $this->char--; - $this->state = 'data'; - - /* Anything else */ - } else { - /* Append the input character to the comment token's data. Stay in - the comment state. */ - $this->token['data'] .= $char; - } - } - - private function commentDashState() - { - /* Consume the next input character: */ - $this->char++; - $char = $this->char(); - - /* U+002D HYPHEN-MINUS (-) */ - if ($char === '-') { - /* Switch to the comment end state */ - $this->state = 'commentEnd'; - - /* EOF */ - } elseif ($this->char === $this->EOF) { - /* Parse error. Emit the comment token. Reconsume the EOF character - in the data state. */ - $this->emitToken($this->token); - $this->char--; - $this->state = 'data'; - - /* Anything else */ - } else { - /* Append a U+002D HYPHEN-MINUS (-) character and the input - character to the comment token's data. Switch to the comment state. */ - $this->token['data'] .= '-' . $char; - $this->state = 'comment'; - } - } - - private function commentEndState() - { - /* Consume the next input character: */ - $this->char++; - $char = $this->char(); - - if ($char === '>') { - $this->emitToken($this->token); - $this->state = 'data'; - - } elseif ($char === '-') { - $this->token['data'] .= '-'; - - } elseif ($this->char === $this->EOF) { - $this->emitToken($this->token); - $this->char--; - $this->state = 'data'; - - } else { - $this->token['data'] .= '--' . $char; - $this->state = 'comment'; - } - } - - private function doctypeState() - { - /* Consume the next input character: */ - $this->char++; - $char = $this->char(); - - if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - $this->state = 'beforeDoctypeName'; - - } else { - $this->char--; - $this->state = 'beforeDoctypeName'; - } - } - - private function beforeDoctypeNameState() - { - /* Consume the next input character: */ - $this->char++; - $char = $this->char(); - - if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - // Stay in the before DOCTYPE name state. - - } elseif (preg_match('/^[a-z]$/', $char)) { - $this->token = array( - 'name' => strtoupper($char), - 'type' => self::DOCTYPE, - 'error' => true - ); - - $this->state = 'doctypeName'; - - } elseif ($char === '>') { - $this->emitToken( - array( - 'name' => null, - 'type' => self::DOCTYPE, - 'error' => true - ) - ); - - $this->state = 'data'; - - } elseif ($this->char === $this->EOF) { - $this->emitToken( - array( - 'name' => null, - 'type' => self::DOCTYPE, - 'error' => true - ) - ); - - $this->char--; - $this->state = 'data'; - - } else { - $this->token = array( - 'name' => $char, - 'type' => self::DOCTYPE, - 'error' => true - ); - - $this->state = 'doctypeName'; - } - } - - private function doctypeNameState() - { - /* Consume the next input character: */ - $this->char++; - $char = $this->char(); - - if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - $this->state = 'AfterDoctypeName'; - - } elseif ($char === '>') { - $this->emitToken($this->token); - $this->state = 'data'; - - } elseif (preg_match('/^[a-z]$/', $char)) { - $this->token['name'] .= strtoupper($char); - - } elseif ($this->char === $this->EOF) { - $this->emitToken($this->token); - $this->char--; - $this->state = 'data'; - - } else { - $this->token['name'] .= $char; - } - - $this->token['error'] = ($this->token['name'] === 'HTML') - ? false - : true; - } - - private function afterDoctypeNameState() - { - /* Consume the next input character: */ - $this->char++; - $char = $this->char(); - - if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - // Stay in the DOCTYPE name state. - - } elseif ($char === '>') { - $this->emitToken($this->token); - $this->state = 'data'; - - } elseif ($this->char === $this->EOF) { - $this->emitToken($this->token); - $this->char--; - $this->state = 'data'; - - } else { - $this->token['error'] = true; - $this->state = 'bogusDoctype'; - } - } - - private function bogusDoctypeState() - { - /* Consume the next input character: */ - $this->char++; - $char = $this->char(); - - if ($char === '>') { - $this->emitToken($this->token); - $this->state = 'data'; - - } elseif ($this->char === $this->EOF) { - $this->emitToken($this->token); - $this->char--; - $this->state = 'data'; - - } else { - // Stay in the bogus DOCTYPE state. - } - } - - private function entity() - { - $start = $this->char; - - // This section defines how to consume an entity. This definition is - // used when parsing entities in text and in attributes. - - // The behaviour depends on the identity of the next character (the - // one immediately after the U+0026 AMPERSAND character): - - switch ($this->character($this->char + 1)) { - // U+0023 NUMBER SIGN (#) - case '#': - - // The behaviour further depends on the character after the - // U+0023 NUMBER SIGN: - switch ($this->character($this->char + 1)) { - // U+0078 LATIN SMALL LETTER X - // U+0058 LATIN CAPITAL LETTER X - case 'x': - case 'X': - // Follow the steps below, but using the range of - // characters U+0030 DIGIT ZERO through to U+0039 DIGIT - // NINE, U+0061 LATIN SMALL LETTER A through to U+0066 - // LATIN SMALL LETTER F, and U+0041 LATIN CAPITAL LETTER - // A, through to U+0046 LATIN CAPITAL LETTER F (in other - // words, 0-9, A-F, a-f). - $char = 1; - $char_class = '0-9A-Fa-f'; - break; - - // Anything else - default: - // Follow the steps below, but using the range of - // characters U+0030 DIGIT ZERO through to U+0039 DIGIT - // NINE (i.e. just 0-9). - $char = 0; - $char_class = '0-9'; - break; - } - - // Consume as many characters as match the range of characters - // given above. - $this->char++; - $e_name = $this->characters($char_class, $this->char + $char + 1); - $entity = $this->character($start, $this->char); - $cond = strlen($e_name) > 0; - - // The rest of the parsing happens below. - break; - - // Anything else - default: - // Consume the maximum number of characters possible, with the - // consumed characters case-sensitively matching one of the - // identifiers in the first column of the entities table. - - $e_name = $this->characters('0-9A-Za-z;', $this->char + 1); - $len = strlen($e_name); - - for ($c = 1; $c <= $len; $c++) { - $id = substr($e_name, 0, $c); - $this->char++; - - if (in_array($id, $this->entities)) { - if ($e_name[$c - 1] !== ';') { - if ($c < $len && $e_name[$c] == ';') { - $this->char++; // consume extra semicolon - } - } - $entity = $id; - break; - } - } - - $cond = isset($entity); - // The rest of the parsing happens below. - break; - } - - if (!$cond) { - // If no match can be made, then this is a parse error. No - // characters are consumed, and nothing is returned. - $this->char = $start; - return false; - } - - // Return a character token for the character corresponding to the - // entity name (as given by the second column of the entities table). - return html_entity_decode('&' . rtrim($entity, ';') . ';', ENT_QUOTES, 'UTF-8'); - } - - private function emitToken($token) - { - $emit = $this->tree->emitToken($token); - - if (is_int($emit)) { - $this->content_model = $emit; - - } elseif ($token['type'] === self::ENDTAG) { - $this->content_model = self::PCDATA; - } - } - - private function EOF() - { - $this->state = null; - $this->tree->emitToken( - array( - 'type' => self::EOF - ) - ); - } -} - -class HTML5TreeConstructer -{ - public $stack = array(); - - private $phase; - private $mode; - private $dom; - private $foster_parent = null; - private $a_formatting = array(); - - private $head_pointer = null; - private $form_pointer = null; - - private $scoping = array('button', 'caption', 'html', 'marquee', 'object', 'table', 'td', 'th'); - private $formatting = array( - 'a', - 'b', - 'big', - 'em', - 'font', - 'i', - 'nobr', - 's', - 'small', - 'strike', - 'strong', - 'tt', - 'u' - ); - private $special = array( - 'address', - 'area', - 'base', - 'basefont', - 'bgsound', - 'blockquote', - 'body', - 'br', - 'center', - 'col', - 'colgroup', - 'dd', - 'dir', - 'div', - 'dl', - 'dt', - 'embed', - 'fieldset', - 'form', - 'frame', - 'frameset', - 'h1', - 'h2', - 'h3', - 'h4', - 'h5', - 'h6', - 'head', - 'hr', - 'iframe', - 'image', - 'img', - 'input', - 'isindex', - 'li', - 'link', - 'listing', - 'menu', - 'meta', - 'noembed', - 'noframes', - 'noscript', - 'ol', - 'optgroup', - 'option', - 'p', - 'param', - 'plaintext', - 'pre', - 'script', - 'select', - 'spacer', - 'style', - 'tbody', - 'textarea', - 'tfoot', - 'thead', - 'title', - 'tr', - 'ul', - 'wbr' - ); - - // The different phases. - const INIT_PHASE = 0; - const ROOT_PHASE = 1; - const MAIN_PHASE = 2; - const END_PHASE = 3; - - // The different insertion modes for the main phase. - const BEFOR_HEAD = 0; - const IN_HEAD = 1; - const AFTER_HEAD = 2; - const IN_BODY = 3; - const IN_TABLE = 4; - const IN_CAPTION = 5; - const IN_CGROUP = 6; - const IN_TBODY = 7; - const IN_ROW = 8; - const IN_CELL = 9; - const IN_SELECT = 10; - const AFTER_BODY = 11; - const IN_FRAME = 12; - const AFTR_FRAME = 13; - - // The different types of elements. - const SPECIAL = 0; - const SCOPING = 1; - const FORMATTING = 2; - const PHRASING = 3; - - const MARKER = 0; - - public function __construct() - { - $this->phase = self::INIT_PHASE; - $this->mode = self::BEFOR_HEAD; - $this->dom = new DOMDocument; - - $this->dom->encoding = 'UTF-8'; - $this->dom->preserveWhiteSpace = true; - $this->dom->substituteEntities = true; - $this->dom->strictErrorChecking = false; - } - - // Process tag tokens - public function emitToken($token) - { - switch ($this->phase) { - case self::INIT_PHASE: - return $this->initPhase($token); - break; - case self::ROOT_PHASE: - return $this->rootElementPhase($token); - break; - case self::MAIN_PHASE: - return $this->mainPhase($token); - break; - case self::END_PHASE : - return $this->trailingEndPhase($token); - break; - } - } - - private function initPhase($token) - { - /* Initially, the tree construction stage must handle each token - emitted from the tokenisation stage as follows: */ - - /* A DOCTYPE token that is marked as being in error - A comment token - A start tag token - An end tag token - A character token that is not one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE - An end-of-file token */ - if ((isset($token['error']) && $token['error']) || - $token['type'] === HTML5::COMMENT || - $token['type'] === HTML5::STARTTAG || - $token['type'] === HTML5::ENDTAG || - $token['type'] === HTML5::EOF || - ($token['type'] === HTML5::CHARACTR && isset($token['data']) && - !preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) - ) { - /* This specification does not define how to handle this case. In - particular, user agents may ignore the entirety of this specification - altogether for such documents, and instead invoke special parse modes - with a greater emphasis on backwards compatibility. */ - - $this->phase = self::ROOT_PHASE; - return $this->rootElementPhase($token); - - /* A DOCTYPE token marked as being correct */ - } elseif (isset($token['error']) && !$token['error']) { - /* Append a DocumentType node to the Document node, with the name - attribute set to the name given in the DOCTYPE token (which will be - "HTML"), and the other attributes specific to DocumentType objects - set to null, empty lists, or the empty string as appropriate. */ - $doctype = new DOMDocumentType(null, null, 'HTML'); - - /* Then, switch to the root element phase of the tree construction - stage. */ - $this->phase = self::ROOT_PHASE; - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - } elseif (isset($token['data']) && preg_match( - '/^[\t\n\x0b\x0c ]+$/', - $token['data'] - ) - ) { - /* Append that character to the Document node. */ - $text = $this->dom->createTextNode($token['data']); - $this->dom->appendChild($text); - } - } - - private function rootElementPhase($token) - { - /* After the initial phase, as each token is emitted from the tokenisation - stage, it must be processed as described in this section. */ - - /* A DOCTYPE token */ - if ($token['type'] === HTML5::DOCTYPE) { - // Parse error. Ignore the token. - - /* A comment token */ - } elseif ($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the Document object with the data - attribute set to the data given in the comment token. */ - $comment = $this->dom->createComment($token['data']); - $this->dom->appendChild($comment); - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - } elseif ($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) - ) { - /* Append that character to the Document node. */ - $text = $this->dom->createTextNode($token['data']); - $this->dom->appendChild($text); - - /* A character token that is not one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED - (FF), or U+0020 SPACE - A start tag token - An end tag token - An end-of-file token */ - } elseif (($token['type'] === HTML5::CHARACTR && - !preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) || - $token['type'] === HTML5::STARTTAG || - $token['type'] === HTML5::ENDTAG || - $token['type'] === HTML5::EOF - ) { - /* Create an HTMLElement node with the tag name html, in the HTML - namespace. Append it to the Document object. Switch to the main - phase and reprocess the current token. */ - $html = $this->dom->createElement('html'); - $this->dom->appendChild($html); - $this->stack[] = $html; - - $this->phase = self::MAIN_PHASE; - return $this->mainPhase($token); - } - } - - private function mainPhase($token) - { - /* Tokens in the main phase must be handled as follows: */ - - /* A DOCTYPE token */ - if ($token['type'] === HTML5::DOCTYPE) { - // Parse error. Ignore the token. - - /* A start tag token with the tag name "html" */ - } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'html') { - /* If this start tag token was not the first start tag token, then - it is a parse error. */ - - /* For each attribute on the token, check to see if the attribute - is already present on the top element of the stack of open elements. - If it is not, add the attribute and its corresponding value to that - element. */ - foreach ($token['attr'] as $attr) { - if (!$this->stack[0]->hasAttribute($attr['name'])) { - $this->stack[0]->setAttribute($attr['name'], $attr['value']); - } - } - - /* An end-of-file token */ - } elseif ($token['type'] === HTML5::EOF) { - /* Generate implied end tags. */ - $this->generateImpliedEndTags(); - - /* Anything else. */ - } else { - /* Depends on the insertion mode: */ - switch ($this->mode) { - case self::BEFOR_HEAD: - return $this->beforeHead($token); - break; - case self::IN_HEAD: - return $this->inHead($token); - break; - case self::AFTER_HEAD: - return $this->afterHead($token); - break; - case self::IN_BODY: - return $this->inBody($token); - break; - case self::IN_TABLE: - return $this->inTable($token); - break; - case self::IN_CAPTION: - return $this->inCaption($token); - break; - case self::IN_CGROUP: - return $this->inColumnGroup($token); - break; - case self::IN_TBODY: - return $this->inTableBody($token); - break; - case self::IN_ROW: - return $this->inRow($token); - break; - case self::IN_CELL: - return $this->inCell($token); - break; - case self::IN_SELECT: - return $this->inSelect($token); - break; - case self::AFTER_BODY: - return $this->afterBody($token); - break; - case self::IN_FRAME: - return $this->inFrameset($token); - break; - case self::AFTR_FRAME: - return $this->afterFrameset($token); - break; - case self::END_PHASE: - return $this->trailingEndPhase($token); - break; - } - } - } - - private function beforeHead($token) - { - /* Handle the token as follows: */ - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - if ($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) - ) { - /* Append the character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif ($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the current node with the data attribute - set to the data given in the comment token. */ - $this->insertComment($token['data']); - - /* A start tag token with the tag name "head" */ - } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'head') { - /* Create an element for the token, append the new element to the - current node and push it onto the stack of open elements. */ - $element = $this->insertElement($token); - - /* Set the head element pointer to this new element node. */ - $this->head_pointer = $element; - - /* Change the insertion mode to "in head". */ - $this->mode = self::IN_HEAD; - - /* A start tag token whose tag name is one of: "base", "link", "meta", - "script", "style", "title". Or an end tag with the tag name "html". - Or a character token that is not one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE. Or any other start tag token */ - } elseif ($token['type'] === HTML5::STARTTAG || - ($token['type'] === HTML5::ENDTAG && $token['name'] === 'html') || - ($token['type'] === HTML5::CHARACTR && !preg_match( - '/^[\t\n\x0b\x0c ]$/', - $token['data'] - )) - ) { - /* Act as if a start tag token with the tag name "head" and no - attributes had been seen, then reprocess the current token. */ - $this->beforeHead( - array( - 'name' => 'head', - 'type' => HTML5::STARTTAG, - 'attr' => array() - ) - ); - - return $this->inHead($token); - - /* Any other end tag */ - } elseif ($token['type'] === HTML5::ENDTAG) { - /* Parse error. Ignore the token. */ - } - } - - private function inHead($token) - { - /* Handle the token as follows: */ - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE. - - THIS DIFFERS FROM THE SPEC: If the current node is either a title, style - or script element, append the character to the current node regardless - of its content. */ - if (($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) || ( - $token['type'] === HTML5::CHARACTR && in_array( - end($this->stack)->nodeName, - array('title', 'style', 'script') - )) - ) { - /* Append the character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif ($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the current node with the data attribute - set to the data given in the comment token. */ - $this->insertComment($token['data']); - - } elseif ($token['type'] === HTML5::ENDTAG && - in_array($token['name'], array('title', 'style', 'script')) - ) { - array_pop($this->stack); - return HTML5::PCDATA; - - /* A start tag with the tag name "title" */ - } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'title') { - /* Create an element for the token and append the new element to the - node pointed to by the head element pointer, or, if that is null - (innerHTML case), to the current node. */ - if ($this->head_pointer !== null) { - $element = $this->insertElement($token, false); - $this->head_pointer->appendChild($element); - - } else { - $element = $this->insertElement($token); - } - - /* Switch the tokeniser's content model flag to the RCDATA state. */ - return HTML5::RCDATA; - - /* A start tag with the tag name "style" */ - } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'style') { - /* Create an element for the token and append the new element to the - node pointed to by the head element pointer, or, if that is null - (innerHTML case), to the current node. */ - if ($this->head_pointer !== null) { - $element = $this->insertElement($token, false); - $this->head_pointer->appendChild($element); - - } else { - $this->insertElement($token); - } - - /* Switch the tokeniser's content model flag to the CDATA state. */ - return HTML5::CDATA; - - /* A start tag with the tag name "script" */ - } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'script') { - /* Create an element for the token. */ - $element = $this->insertElement($token, false); - $this->head_pointer->appendChild($element); - - /* Switch the tokeniser's content model flag to the CDATA state. */ - return HTML5::CDATA; - - /* A start tag with the tag name "base", "link", or "meta" */ - } elseif ($token['type'] === HTML5::STARTTAG && in_array( - $token['name'], - array('base', 'link', 'meta') - ) - ) { - /* Create an element for the token and append the new element to the - node pointed to by the head element pointer, or, if that is null - (innerHTML case), to the current node. */ - if ($this->head_pointer !== null) { - $element = $this->insertElement($token, false); - $this->head_pointer->appendChild($element); - array_pop($this->stack); - - } else { - $this->insertElement($token); - } - - /* An end tag with the tag name "head" */ - } elseif ($token['type'] === HTML5::ENDTAG && $token['name'] === 'head') { - /* If the current node is a head element, pop the current node off - the stack of open elements. */ - if ($this->head_pointer->isSameNode(end($this->stack))) { - array_pop($this->stack); - - /* Otherwise, this is a parse error. */ - } else { - // k - } - - /* Change the insertion mode to "after head". */ - $this->mode = self::AFTER_HEAD; - - /* A start tag with the tag name "head" or an end tag except "html". */ - } elseif (($token['type'] === HTML5::STARTTAG && $token['name'] === 'head') || - ($token['type'] === HTML5::ENDTAG && $token['name'] !== 'html') - ) { - // Parse error. Ignore the token. - - /* Anything else */ - } else { - /* If the current node is a head element, act as if an end tag - token with the tag name "head" had been seen. */ - if ($this->head_pointer->isSameNode(end($this->stack))) { - $this->inHead( - array( - 'name' => 'head', - 'type' => HTML5::ENDTAG - ) - ); - - /* Otherwise, change the insertion mode to "after head". */ - } else { - $this->mode = self::AFTER_HEAD; - } - - /* Then, reprocess the current token. */ - return $this->afterHead($token); - } - } - - private function afterHead($token) - { - /* Handle the token as follows: */ - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - if ($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) - ) { - /* Append the character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif ($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the current node with the data attribute - set to the data given in the comment token. */ - $this->insertComment($token['data']); - - /* A start tag token with the tag name "body" */ - } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'body') { - /* Insert a body element for the token. */ - $this->insertElement($token); - - /* Change the insertion mode to "in body". */ - $this->mode = self::IN_BODY; - - /* A start tag token with the tag name "frameset" */ - } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'frameset') { - /* Insert a frameset element for the token. */ - $this->insertElement($token); - - /* Change the insertion mode to "in frameset". */ - $this->mode = self::IN_FRAME; - - /* A start tag token whose tag name is one of: "base", "link", "meta", - "script", "style", "title" */ - } elseif ($token['type'] === HTML5::STARTTAG && in_array( - $token['name'], - array('base', 'link', 'meta', 'script', 'style', 'title') - ) - ) { - /* Parse error. Switch the insertion mode back to "in head" and - reprocess the token. */ - $this->mode = self::IN_HEAD; - return $this->inHead($token); - - /* Anything else */ - } else { - /* Act as if a start tag token with the tag name "body" and no - attributes had been seen, and then reprocess the current token. */ - $this->afterHead( - array( - 'name' => 'body', - 'type' => HTML5::STARTTAG, - 'attr' => array() - ) - ); - - return $this->inBody($token); - } - } - - private function inBody($token) - { - /* Handle the token as follows: */ - - switch ($token['type']) { - /* A character token */ - case HTML5::CHARACTR: - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Append the token's character to the current node. */ - $this->insertText($token['data']); - break; - - /* A comment token */ - case HTML5::COMMENT: - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $this->insertComment($token['data']); - break; - - case HTML5::STARTTAG: - switch ($token['name']) { - /* A start tag token whose tag name is one of: "script", - "style" */ - case 'script': - case 'style': - /* Process the token as if the insertion mode had been "in - head". */ - return $this->inHead($token); - break; - - /* A start tag token whose tag name is one of: "base", "link", - "meta", "title" */ - case 'base': - case 'link': - case 'meta': - case 'title': - /* Parse error. Process the token as if the insertion mode - had been "in head". */ - return $this->inHead($token); - break; - - /* A start tag token with the tag name "body" */ - case 'body': - /* Parse error. If the second element on the stack of open - elements is not a body element, or, if the stack of open - elements has only one node on it, then ignore the token. - (innerHTML case) */ - if (count($this->stack) === 1 || $this->stack[1]->nodeName !== 'body') { - // Ignore - - /* Otherwise, for each attribute on the token, check to see - if the attribute is already present on the body element (the - second element) on the stack of open elements. If it is not, - add the attribute and its corresponding value to that - element. */ - } else { - foreach ($token['attr'] as $attr) { - if (!$this->stack[1]->hasAttribute($attr['name'])) { - $this->stack[1]->setAttribute($attr['name'], $attr['value']); - } - } - } - break; - - /* A start tag whose tag name is one of: "address", - "blockquote", "center", "dir", "div", "dl", "fieldset", - "listing", "menu", "ol", "p", "ul" */ - case 'address': - case 'blockquote': - case 'center': - case 'dir': - case 'div': - case 'dl': - case 'fieldset': - case 'listing': - case 'menu': - case 'ol': - case 'p': - case 'ul': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been - seen. */ - if ($this->elementInScope('p')) { - $this->emitToken( - array( - 'name' => 'p', - 'type' => HTML5::ENDTAG - ) - ); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - break; - - /* A start tag whose tag name is "form" */ - case 'form': - /* If the form element pointer is not null, ignore the - token with a parse error. */ - if ($this->form_pointer !== null) { - // Ignore. - - /* Otherwise: */ - } else { - /* If the stack of open elements has a p element in - scope, then act as if an end tag with the tag name p - had been seen. */ - if ($this->elementInScope('p')) { - $this->emitToken( - array( - 'name' => 'p', - 'type' => HTML5::ENDTAG - ) - ); - } - - /* Insert an HTML element for the token, and set the - form element pointer to point to the element created. */ - $element = $this->insertElement($token); - $this->form_pointer = $element; - } - break; - - /* A start tag whose tag name is "li", "dd" or "dt" */ - case 'li': - case 'dd': - case 'dt': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been - seen. */ - if ($this->elementInScope('p')) { - $this->emitToken( - array( - 'name' => 'p', - 'type' => HTML5::ENDTAG - ) - ); - } - - $stack_length = count($this->stack) - 1; - - for ($n = $stack_length; 0 <= $n; $n--) { - /* 1. Initialise node to be the current node (the - bottommost node of the stack). */ - $stop = false; - $node = $this->stack[$n]; - $cat = $this->getElementCategory($node->tagName); - - /* 2. If node is an li, dd or dt element, then pop all - the nodes from the current node up to node, including - node, then stop this algorithm. */ - if ($token['name'] === $node->tagName || ($token['name'] !== 'li' - && ($node->tagName === 'dd' || $node->tagName === 'dt')) - ) { - for ($x = $stack_length; $x >= $n; $x--) { - array_pop($this->stack); - } - - break; - } - - /* 3. If node is not in the formatting category, and is - not in the phrasing category, and is not an address or - div element, then stop this algorithm. */ - if ($cat !== self::FORMATTING && $cat !== self::PHRASING && - $node->tagName !== 'address' && $node->tagName !== 'div' - ) { - break; - } - } - - /* Finally, insert an HTML element with the same tag - name as the token's. */ - $this->insertElement($token); - break; - - /* A start tag token whose tag name is "plaintext" */ - case 'plaintext': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been - seen. */ - if ($this->elementInScope('p')) { - $this->emitToken( - array( - 'name' => 'p', - 'type' => HTML5::ENDTAG - ) - ); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - return HTML5::PLAINTEXT; - break; - - /* A start tag whose tag name is one of: "h1", "h2", "h3", "h4", - "h5", "h6" */ - case 'h1': - case 'h2': - case 'h3': - case 'h4': - case 'h5': - case 'h6': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been seen. */ - if ($this->elementInScope('p')) { - $this->emitToken( - array( - 'name' => 'p', - 'type' => HTML5::ENDTAG - ) - ); - } - - /* If the stack of open elements has in scope an element whose - tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then - this is a parse error; pop elements from the stack until an - element with one of those tag names has been popped from the - stack. */ - while ($this->elementInScope(array('h1', 'h2', 'h3', 'h4', 'h5', 'h6'))) { - array_pop($this->stack); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - break; - - /* A start tag whose tag name is "a" */ - case 'a': - /* If the list of active formatting elements contains - an element whose tag name is "a" between the end of the - list and the last marker on the list (or the start of - the list if there is no marker on the list), then this - is a parse error; act as if an end tag with the tag name - "a" had been seen, then remove that element from the list - of active formatting elements and the stack of open - elements if the end tag didn't already remove it (it - might not have if the element is not in table scope). */ - $leng = count($this->a_formatting); - - for ($n = $leng - 1; $n >= 0; $n--) { - if ($this->a_formatting[$n] === self::MARKER) { - break; - - } elseif ($this->a_formatting[$n]->nodeName === 'a') { - $this->emitToken( - array( - 'name' => 'a', - 'type' => HTML5::ENDTAG - ) - ); - break; - } - } - - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $el = $this->insertElement($token); - - /* Add that element to the list of active formatting - elements. */ - $this->a_formatting[] = $el; - break; - - /* A start tag whose tag name is one of: "b", "big", "em", "font", - "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */ - case 'b': - case 'big': - case 'em': - case 'font': - case 'i': - case 'nobr': - case 's': - case 'small': - case 'strike': - case 'strong': - case 'tt': - case 'u': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $el = $this->insertElement($token); - - /* Add that element to the list of active formatting - elements. */ - $this->a_formatting[] = $el; - break; - - /* A start tag token whose tag name is "button" */ - case 'button': - /* If the stack of open elements has a button element in scope, - then this is a parse error; act as if an end tag with the tag - name "button" had been seen, then reprocess the token. (We don't - do that. Unnecessary.) */ - if ($this->elementInScope('button')) { - $this->inBody( - array( - 'name' => 'button', - 'type' => HTML5::ENDTAG - ) - ); - } - - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Insert a marker at the end of the list of active - formatting elements. */ - $this->a_formatting[] = self::MARKER; - break; - - /* A start tag token whose tag name is one of: "marquee", "object" */ - case 'marquee': - case 'object': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Insert a marker at the end of the list of active - formatting elements. */ - $this->a_formatting[] = self::MARKER; - break; - - /* A start tag token whose tag name is "xmp" */ - case 'xmp': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Switch the content model flag to the CDATA state. */ - return HTML5::CDATA; - break; - - /* A start tag whose tag name is "table" */ - case 'table': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been seen. */ - if ($this->elementInScope('p')) { - $this->emitToken( - array( - 'name' => 'p', - 'type' => HTML5::ENDTAG - ) - ); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Change the insertion mode to "in table". */ - $this->mode = self::IN_TABLE; - break; - - /* A start tag whose tag name is one of: "area", "basefont", - "bgsound", "br", "embed", "img", "param", "spacer", "wbr" */ - case 'area': - case 'basefont': - case 'bgsound': - case 'br': - case 'embed': - case 'img': - case 'param': - case 'spacer': - case 'wbr': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Immediately pop the current node off the stack of open elements. */ - array_pop($this->stack); - break; - - /* A start tag whose tag name is "hr" */ - case 'hr': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been seen. */ - if ($this->elementInScope('p')) { - $this->emitToken( - array( - 'name' => 'p', - 'type' => HTML5::ENDTAG - ) - ); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Immediately pop the current node off the stack of open elements. */ - array_pop($this->stack); - break; - - /* A start tag whose tag name is "image" */ - case 'image': - /* Parse error. Change the token's tag name to "img" and - reprocess it. (Don't ask.) */ - $token['name'] = 'img'; - return $this->inBody($token); - break; - - /* A start tag whose tag name is "input" */ - case 'input': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an input element for the token. */ - $element = $this->insertElement($token, false); - - /* If the form element pointer is not null, then associate the - input element with the form element pointed to by the form - element pointer. */ - $this->form_pointer !== null - ? $this->form_pointer->appendChild($element) - : end($this->stack)->appendChild($element); - - /* Pop that input element off the stack of open elements. */ - array_pop($this->stack); - break; - - /* A start tag whose tag name is "isindex" */ - case 'isindex': - /* Parse error. */ - // w/e - - /* If the form element pointer is not null, - then ignore the token. */ - if ($this->form_pointer === null) { - /* Act as if a start tag token with the tag name "form" had - been seen. */ - $this->inBody( - array( - 'name' => 'body', - 'type' => HTML5::STARTTAG, - 'attr' => array() - ) - ); - - /* Act as if a start tag token with the tag name "hr" had - been seen. */ - $this->inBody( - array( - 'name' => 'hr', - 'type' => HTML5::STARTTAG, - 'attr' => array() - ) - ); - - /* Act as if a start tag token with the tag name "p" had - been seen. */ - $this->inBody( - array( - 'name' => 'p', - 'type' => HTML5::STARTTAG, - 'attr' => array() - ) - ); - - /* Act as if a start tag token with the tag name "label" - had been seen. */ - $this->inBody( - array( - 'name' => 'label', - 'type' => HTML5::STARTTAG, - 'attr' => array() - ) - ); - - /* Act as if a stream of character tokens had been seen. */ - $this->insertText( - 'This is a searchable index. ' . - 'Insert your search keywords here: ' - ); - - /* Act as if a start tag token with the tag name "input" - had been seen, with all the attributes from the "isindex" - token, except with the "name" attribute set to the value - "isindex" (ignoring any explicit "name" attribute). */ - $attr = $token['attr']; - $attr[] = array('name' => 'name', 'value' => 'isindex'); - - $this->inBody( - array( - 'name' => 'input', - 'type' => HTML5::STARTTAG, - 'attr' => $attr - ) - ); - - /* Act as if a stream of character tokens had been seen - (see below for what they should say). */ - $this->insertText( - 'This is a searchable index. ' . - 'Insert your search keywords here: ' - ); - - /* Act as if an end tag token with the tag name "label" - had been seen. */ - $this->inBody( - array( - 'name' => 'label', - 'type' => HTML5::ENDTAG - ) - ); - - /* Act as if an end tag token with the tag name "p" had - been seen. */ - $this->inBody( - array( - 'name' => 'p', - 'type' => HTML5::ENDTAG - ) - ); - - /* Act as if a start tag token with the tag name "hr" had - been seen. */ - $this->inBody( - array( - 'name' => 'hr', - 'type' => HTML5::ENDTAG - ) - ); - - /* Act as if an end tag token with the tag name "form" had - been seen. */ - $this->inBody( - array( - 'name' => 'form', - 'type' => HTML5::ENDTAG - ) - ); - } - break; - - /* A start tag whose tag name is "textarea" */ - case 'textarea': - $this->insertElement($token); - - /* Switch the tokeniser's content model flag to the - RCDATA state. */ - return HTML5::RCDATA; - break; - - /* A start tag whose tag name is one of: "iframe", "noembed", - "noframes" */ - case 'iframe': - case 'noembed': - case 'noframes': - $this->insertElement($token); - - /* Switch the tokeniser's content model flag to the CDATA state. */ - return HTML5::CDATA; - break; - - /* A start tag whose tag name is "select" */ - case 'select': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Change the insertion mode to "in select". */ - $this->mode = self::IN_SELECT; - break; - - /* A start or end tag whose tag name is one of: "caption", "col", - "colgroup", "frame", "frameset", "head", "option", "optgroup", - "tbody", "td", "tfoot", "th", "thead", "tr". */ - case 'caption': - case 'col': - case 'colgroup': - case 'frame': - case 'frameset': - case 'head': - case 'option': - case 'optgroup': - case 'tbody': - case 'td': - case 'tfoot': - case 'th': - case 'thead': - case 'tr': - // Parse error. Ignore the token. - break; - - /* A start or end tag whose tag name is one of: "event-source", - "section", "nav", "article", "aside", "header", "footer", - "datagrid", "command" */ - case 'event-source': - case 'section': - case 'nav': - case 'article': - case 'aside': - case 'header': - case 'footer': - case 'datagrid': - case 'command': - // Work in progress! - break; - - /* A start tag token not covered by the previous entries */ - default: - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - $this->insertElement($token, true, true); - break; - } - break; - - case HTML5::ENDTAG: - switch ($token['name']) { - /* An end tag with the tag name "body" */ - case 'body': - /* If the second element in the stack of open elements is - not a body element, this is a parse error. Ignore the token. - (innerHTML case) */ - if (count($this->stack) < 2 || $this->stack[1]->nodeName !== 'body') { - // Ignore. - - /* If the current node is not the body element, then this - is a parse error. */ - } elseif (end($this->stack)->nodeName !== 'body') { - // Parse error. - } - - /* Change the insertion mode to "after body". */ - $this->mode = self::AFTER_BODY; - break; - - /* An end tag with the tag name "html" */ - case 'html': - /* Act as if an end tag with tag name "body" had been seen, - then, if that token wasn't ignored, reprocess the current - token. */ - $this->inBody( - array( - 'name' => 'body', - 'type' => HTML5::ENDTAG - ) - ); - - return $this->afterBody($token); - break; - - /* An end tag whose tag name is one of: "address", "blockquote", - "center", "dir", "div", "dl", "fieldset", "listing", "menu", - "ol", "pre", "ul" */ - case 'address': - case 'blockquote': - case 'center': - case 'dir': - case 'div': - case 'dl': - case 'fieldset': - case 'listing': - case 'menu': - case 'ol': - case 'pre': - case 'ul': - /* If the stack of open elements has an element in scope - with the same tag name as that of the token, then generate - implied end tags. */ - if ($this->elementInScope($token['name'])) { - $this->generateImpliedEndTags(); - - /* Now, if the current node is not an element with - the same tag name as that of the token, then this - is a parse error. */ - // w/e - - /* If the stack of open elements has an element in - scope with the same tag name as that of the token, - then pop elements from this stack until an element - with that tag name has been popped from the stack. */ - for ($n = count($this->stack) - 1; $n >= 0; $n--) { - if ($this->stack[$n]->nodeName === $token['name']) { - $n = -1; - } - - array_pop($this->stack); - } - } - break; - - /* An end tag whose tag name is "form" */ - case 'form': - /* If the stack of open elements has an element in scope - with the same tag name as that of the token, then generate - implied end tags. */ - if ($this->elementInScope($token['name'])) { - $this->generateImpliedEndTags(); - - } - - if (end($this->stack)->nodeName !== $token['name']) { - /* Now, if the current node is not an element with the - same tag name as that of the token, then this is a parse - error. */ - // w/e - - } else { - /* Otherwise, if the current node is an element with - the same tag name as that of the token pop that element - from the stack. */ - array_pop($this->stack); - } - - /* In any case, set the form element pointer to null. */ - $this->form_pointer = null; - break; - - /* An end tag whose tag name is "p" */ - case 'p': - /* If the stack of open elements has a p element in scope, - then generate implied end tags, except for p elements. */ - if ($this->elementInScope('p')) { - $this->generateImpliedEndTags(array('p')); - - /* If the current node is not a p element, then this is - a parse error. */ - // k - - /* If the stack of open elements has a p element in - scope, then pop elements from this stack until the stack - no longer has a p element in scope. */ - for ($n = count($this->stack) - 1; $n >= 0; $n--) { - if ($this->elementInScope('p')) { - array_pop($this->stack); - - } else { - break; - } - } - } - break; - - /* An end tag whose tag name is "dd", "dt", or "li" */ - case 'dd': - case 'dt': - case 'li': - /* If the stack of open elements has an element in scope - whose tag name matches the tag name of the token, then - generate implied end tags, except for elements with the - same tag name as the token. */ - if ($this->elementInScope($token['name'])) { - $this->generateImpliedEndTags(array($token['name'])); - - /* If the current node is not an element with the same - tag name as the token, then this is a parse error. */ - // w/e - - /* If the stack of open elements has an element in scope - whose tag name matches the tag name of the token, then - pop elements from this stack until an element with that - tag name has been popped from the stack. */ - for ($n = count($this->stack) - 1; $n >= 0; $n--) { - if ($this->stack[$n]->nodeName === $token['name']) { - $n = -1; - } - - array_pop($this->stack); - } - } - break; - - /* An end tag whose tag name is one of: "h1", "h2", "h3", "h4", - "h5", "h6" */ - case 'h1': - case 'h2': - case 'h3': - case 'h4': - case 'h5': - case 'h6': - $elements = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6'); - - /* If the stack of open elements has in scope an element whose - tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then - generate implied end tags. */ - if ($this->elementInScope($elements)) { - $this->generateImpliedEndTags(); - - /* Now, if the current node is not an element with the same - tag name as that of the token, then this is a parse error. */ - // w/e - - /* If the stack of open elements has in scope an element - whose tag name is one of "h1", "h2", "h3", "h4", "h5", or - "h6", then pop elements from the stack until an element - with one of those tag names has been popped from the stack. */ - while ($this->elementInScope($elements)) { - array_pop($this->stack); - } - } - break; - - /* An end tag whose tag name is one of: "a", "b", "big", "em", - "font", "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */ - case 'a': - case 'b': - case 'big': - case 'em': - case 'font': - case 'i': - case 'nobr': - case 's': - case 'small': - case 'strike': - case 'strong': - case 'tt': - case 'u': - /* 1. Let the formatting element be the last element in - the list of active formatting elements that: - * is between the end of the list and the last scope - marker in the list, if any, or the start of the list - otherwise, and - * has the same tag name as the token. - */ - while (true) { - for ($a = count($this->a_formatting) - 1; $a >= 0; $a--) { - if ($this->a_formatting[$a] === self::MARKER) { - break; - - } elseif ($this->a_formatting[$a]->tagName === $token['name']) { - $formatting_element = $this->a_formatting[$a]; - $in_stack = in_array($formatting_element, $this->stack, true); - $fe_af_pos = $a; - break; - } - } - - /* If there is no such node, or, if that node is - also in the stack of open elements but the element - is not in scope, then this is a parse error. Abort - these steps. The token is ignored. */ - if (!isset($formatting_element) || ($in_stack && - !$this->elementInScope($token['name'])) - ) { - break; - - /* Otherwise, if there is such a node, but that node - is not in the stack of open elements, then this is a - parse error; remove the element from the list, and - abort these steps. */ - } elseif (isset($formatting_element) && !$in_stack) { - unset($this->a_formatting[$fe_af_pos]); - $this->a_formatting = array_merge($this->a_formatting); - break; - } - - /* 2. Let the furthest block be the topmost node in the - stack of open elements that is lower in the stack - than the formatting element, and is not an element in - the phrasing or formatting categories. There might - not be one. */ - $fe_s_pos = array_search($formatting_element, $this->stack, true); - $length = count($this->stack); - - for ($s = $fe_s_pos + 1; $s < $length; $s++) { - $category = $this->getElementCategory($this->stack[$s]->nodeName); - - if ($category !== self::PHRASING && $category !== self::FORMATTING) { - $furthest_block = $this->stack[$s]; - } - } - - /* 3. If there is no furthest block, then the UA must - skip the subsequent steps and instead just pop all - the nodes from the bottom of the stack of open - elements, from the current node up to the formatting - element, and remove the formatting element from the - list of active formatting elements. */ - if (!isset($furthest_block)) { - for ($n = $length - 1; $n >= $fe_s_pos; $n--) { - array_pop($this->stack); - } - - unset($this->a_formatting[$fe_af_pos]); - $this->a_formatting = array_merge($this->a_formatting); - break; - } - - /* 4. Let the common ancestor be the element - immediately above the formatting element in the stack - of open elements. */ - $common_ancestor = $this->stack[$fe_s_pos - 1]; - - /* 5. If the furthest block has a parent node, then - remove the furthest block from its parent node. */ - if ($furthest_block->parentNode !== null) { - $furthest_block->parentNode->removeChild($furthest_block); - } - - /* 6. Let a bookmark note the position of the - formatting element in the list of active formatting - elements relative to the elements on either side - of it in the list. */ - $bookmark = $fe_af_pos; - - /* 7. Let node and last node be the furthest block. - Follow these steps: */ - $node = $furthest_block; - $last_node = $furthest_block; - - while (true) { - for ($n = array_search($node, $this->stack, true) - 1; $n >= 0; $n--) { - /* 7.1 Let node be the element immediately - prior to node in the stack of open elements. */ - $node = $this->stack[$n]; - - /* 7.2 If node is not in the list of active - formatting elements, then remove node from - the stack of open elements and then go back - to step 1. */ - if (!in_array($node, $this->a_formatting, true)) { - unset($this->stack[$n]); - $this->stack = array_merge($this->stack); - - } else { - break; - } - } - - /* 7.3 Otherwise, if node is the formatting - element, then go to the next step in the overall - algorithm. */ - if ($node === $formatting_element) { - break; - - /* 7.4 Otherwise, if last node is the furthest - block, then move the aforementioned bookmark to - be immediately after the node in the list of - active formatting elements. */ - } elseif ($last_node === $furthest_block) { - $bookmark = array_search($node, $this->a_formatting, true) + 1; - } - - /* 7.5 If node has any children, perform a - shallow clone of node, replace the entry for - node in the list of active formatting elements - with an entry for the clone, replace the entry - for node in the stack of open elements with an - entry for the clone, and let node be the clone. */ - if ($node->hasChildNodes()) { - $clone = $node->cloneNode(); - $s_pos = array_search($node, $this->stack, true); - $a_pos = array_search($node, $this->a_formatting, true); - - $this->stack[$s_pos] = $clone; - $this->a_formatting[$a_pos] = $clone; - $node = $clone; - } - - /* 7.6 Insert last node into node, first removing - it from its previous parent node if any. */ - if ($last_node->parentNode !== null) { - $last_node->parentNode->removeChild($last_node); - } - - $node->appendChild($last_node); - - /* 7.7 Let last node be node. */ - $last_node = $node; - } - - /* 8. Insert whatever last node ended up being in - the previous step into the common ancestor node, - first removing it from its previous parent node if - any. */ - if ($last_node->parentNode !== null) { - $last_node->parentNode->removeChild($last_node); - } - - $common_ancestor->appendChild($last_node); - - /* 9. Perform a shallow clone of the formatting - element. */ - $clone = $formatting_element->cloneNode(); - - /* 10. Take all of the child nodes of the furthest - block and append them to the clone created in the - last step. */ - while ($furthest_block->hasChildNodes()) { - $child = $furthest_block->firstChild; - $furthest_block->removeChild($child); - $clone->appendChild($child); - } - - /* 11. Append that clone to the furthest block. */ - $furthest_block->appendChild($clone); - - /* 12. Remove the formatting element from the list - of active formatting elements, and insert the clone - into the list of active formatting elements at the - position of the aforementioned bookmark. */ - $fe_af_pos = array_search($formatting_element, $this->a_formatting, true); - unset($this->a_formatting[$fe_af_pos]); - $this->a_formatting = array_merge($this->a_formatting); - - $af_part1 = array_slice($this->a_formatting, 0, $bookmark - 1); - $af_part2 = array_slice($this->a_formatting, $bookmark, count($this->a_formatting)); - $this->a_formatting = array_merge($af_part1, array($clone), $af_part2); - - /* 13. Remove the formatting element from the stack - of open elements, and insert the clone into the stack - of open elements immediately after (i.e. in a more - deeply nested position than) the position of the - furthest block in that stack. */ - $fe_s_pos = array_search($formatting_element, $this->stack, true); - $fb_s_pos = array_search($furthest_block, $this->stack, true); - unset($this->stack[$fe_s_pos]); - - $s_part1 = array_slice($this->stack, 0, $fb_s_pos); - $s_part2 = array_slice($this->stack, $fb_s_pos + 1, count($this->stack)); - $this->stack = array_merge($s_part1, array($clone), $s_part2); - - /* 14. Jump back to step 1 in this series of steps. */ - unset($formatting_element, $fe_af_pos, $fe_s_pos, $furthest_block); - } - break; - - /* An end tag token whose tag name is one of: "button", - "marquee", "object" */ - case 'button': - case 'marquee': - case 'object': - /* If the stack of open elements has an element in scope whose - tag name matches the tag name of the token, then generate implied - tags. */ - if ($this->elementInScope($token['name'])) { - $this->generateImpliedEndTags(); - - /* Now, if the current node is not an element with the same - tag name as the token, then this is a parse error. */ - // k - - /* Now, if the stack of open elements has an element in scope - whose tag name matches the tag name of the token, then pop - elements from the stack until that element has been popped from - the stack, and clear the list of active formatting elements up - to the last marker. */ - for ($n = count($this->stack) - 1; $n >= 0; $n--) { - if ($this->stack[$n]->nodeName === $token['name']) { - $n = -1; - } - - array_pop($this->stack); - } - - $marker = end(array_keys($this->a_formatting, self::MARKER, true)); - - for ($n = count($this->a_formatting) - 1; $n > $marker; $n--) { - array_pop($this->a_formatting); - } - } - break; - - /* Or an end tag whose tag name is one of: "area", "basefont", - "bgsound", "br", "embed", "hr", "iframe", "image", "img", - "input", "isindex", "noembed", "noframes", "param", "select", - "spacer", "table", "textarea", "wbr" */ - case 'area': - case 'basefont': - case 'bgsound': - case 'br': - case 'embed': - case 'hr': - case 'iframe': - case 'image': - case 'img': - case 'input': - case 'isindex': - case 'noembed': - case 'noframes': - case 'param': - case 'select': - case 'spacer': - case 'table': - case 'textarea': - case 'wbr': - // Parse error. Ignore the token. - break; - - /* An end tag token not covered by the previous entries */ - default: - for ($n = count($this->stack) - 1; $n >= 0; $n--) { - /* Initialise node to be the current node (the bottommost - node of the stack). */ - $node = end($this->stack); - - /* If node has the same tag name as the end tag token, - then: */ - if ($token['name'] === $node->nodeName) { - /* Generate implied end tags. */ - $this->generateImpliedEndTags(); - - /* If the tag name of the end tag token does not - match the tag name of the current node, this is a - parse error. */ - // k - - /* Pop all the nodes from the current node up to - node, including node, then stop this algorithm. */ - for ($x = count($this->stack) - $n; $x >= $n; $x--) { - array_pop($this->stack); - } - - } else { - $category = $this->getElementCategory($node); - - if ($category !== self::SPECIAL && $category !== self::SCOPING) { - /* Otherwise, if node is in neither the formatting - category nor the phrasing category, then this is a - parse error. Stop this algorithm. The end tag token - is ignored. */ - return false; - } - } - } - break; - } - break; - } - } - - private function inTable($token) - { - $clear = array('html', 'table'); - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - if ($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) - ) { - /* Append the character to the current node. */ - $text = $this->dom->createTextNode($token['data']); - end($this->stack)->appendChild($text); - - /* A comment token */ - } elseif ($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $comment = $this->dom->createComment($token['data']); - end($this->stack)->appendChild($comment); - - /* A start tag whose tag name is "caption" */ - } elseif ($token['type'] === HTML5::STARTTAG && - $token['name'] === 'caption' - ) { - /* Clear the stack back to a table context. */ - $this->clearStackToTableContext($clear); - - /* Insert a marker at the end of the list of active - formatting elements. */ - $this->a_formatting[] = self::MARKER; - - /* Insert an HTML element for the token, then switch the - insertion mode to "in caption". */ - $this->insertElement($token); - $this->mode = self::IN_CAPTION; - - /* A start tag whose tag name is "colgroup" */ - } elseif ($token['type'] === HTML5::STARTTAG && - $token['name'] === 'colgroup' - ) { - /* Clear the stack back to a table context. */ - $this->clearStackToTableContext($clear); - - /* Insert an HTML element for the token, then switch the - insertion mode to "in column group". */ - $this->insertElement($token); - $this->mode = self::IN_CGROUP; - - /* A start tag whose tag name is "col" */ - } elseif ($token['type'] === HTML5::STARTTAG && - $token['name'] === 'col' - ) { - $this->inTable( - array( - 'name' => 'colgroup', - 'type' => HTML5::STARTTAG, - 'attr' => array() - ) - ); - - $this->inColumnGroup($token); - - /* A start tag whose tag name is one of: "tbody", "tfoot", "thead" */ - } elseif ($token['type'] === HTML5::STARTTAG && in_array( - $token['name'], - array('tbody', 'tfoot', 'thead') - ) - ) { - /* Clear the stack back to a table context. */ - $this->clearStackToTableContext($clear); - - /* Insert an HTML element for the token, then switch the insertion - mode to "in table body". */ - $this->insertElement($token); - $this->mode = self::IN_TBODY; - - /* A start tag whose tag name is one of: "td", "th", "tr" */ - } elseif ($token['type'] === HTML5::STARTTAG && - in_array($token['name'], array('td', 'th', 'tr')) - ) { - /* Act as if a start tag token with the tag name "tbody" had been - seen, then reprocess the current token. */ - $this->inTable( - array( - 'name' => 'tbody', - 'type' => HTML5::STARTTAG, - 'attr' => array() - ) - ); - - return $this->inTableBody($token); - - /* A start tag whose tag name is "table" */ - } elseif ($token['type'] === HTML5::STARTTAG && - $token['name'] === 'table' - ) { - /* Parse error. Act as if an end tag token with the tag name "table" - had been seen, then, if that token wasn't ignored, reprocess the - current token. */ - $this->inTable( - array( - 'name' => 'table', - 'type' => HTML5::ENDTAG - ) - ); - - return $this->mainPhase($token); - - /* An end tag whose tag name is "table" */ - } elseif ($token['type'] === HTML5::ENDTAG && - $token['name'] === 'table' - ) { - /* If the stack of open elements does not have an element in table - scope with the same tag name as the token, this is a parse error. - Ignore the token. (innerHTML case) */ - if (!$this->elementInScope($token['name'], true)) { - return false; - - /* Otherwise: */ - } else { - /* Generate implied end tags. */ - $this->generateImpliedEndTags(); - - /* Now, if the current node is not a table element, then this - is a parse error. */ - // w/e - - /* Pop elements from this stack until a table element has been - popped from the stack. */ - while (true) { - $current = end($this->stack)->nodeName; - array_pop($this->stack); - - if ($current === 'table') { - break; - } - } - - /* Reset the insertion mode appropriately. */ - $this->resetInsertionMode(); - } - - /* An end tag whose tag name is one of: "body", "caption", "col", - "colgroup", "html", "tbody", "td", "tfoot", "th", "thead", "tr" */ - } elseif ($token['type'] === HTML5::ENDTAG && in_array( - $token['name'], - array( - 'body', - 'caption', - 'col', - 'colgroup', - 'html', - 'tbody', - 'td', - 'tfoot', - 'th', - 'thead', - 'tr' - ) - ) - ) { - // Parse error. Ignore the token. - - /* Anything else */ - } else { - /* Parse error. Process the token as if the insertion mode was "in - body", with the following exception: */ - - /* If the current node is a table, tbody, tfoot, thead, or tr - element, then, whenever a node would be inserted into the current - node, it must instead be inserted into the foster parent element. */ - if (in_array( - end($this->stack)->nodeName, - array('table', 'tbody', 'tfoot', 'thead', 'tr') - ) - ) { - /* The foster parent element is the parent element of the last - table element in the stack of open elements, if there is a - table element and it has such a parent element. If there is no - table element in the stack of open elements (innerHTML case), - then the foster parent element is the first element in the - stack of open elements (the html element). Otherwise, if there - is a table element in the stack of open elements, but the last - table element in the stack of open elements has no parent, or - its parent node is not an element, then the foster parent - element is the element before the last table element in the - stack of open elements. */ - for ($n = count($this->stack) - 1; $n >= 0; $n--) { - if ($this->stack[$n]->nodeName === 'table') { - $table = $this->stack[$n]; - break; - } - } - - if (isset($table) && $table->parentNode !== null) { - $this->foster_parent = $table->parentNode; - - } elseif (!isset($table)) { - $this->foster_parent = $this->stack[0]; - - } elseif (isset($table) && ($table->parentNode === null || - $table->parentNode->nodeType !== XML_ELEMENT_NODE) - ) { - $this->foster_parent = $this->stack[$n - 1]; - } - } - - $this->inBody($token); - } - } - - private function inCaption($token) - { - /* An end tag whose tag name is "caption" */ - if ($token['type'] === HTML5::ENDTAG && $token['name'] === 'caption') { - /* If the stack of open elements does not have an element in table - scope with the same tag name as the token, this is a parse error. - Ignore the token. (innerHTML case) */ - if (!$this->elementInScope($token['name'], true)) { - // Ignore - - /* Otherwise: */ - } else { - /* Generate implied end tags. */ - $this->generateImpliedEndTags(); - - /* Now, if the current node is not a caption element, then this - is a parse error. */ - // w/e - - /* Pop elements from this stack until a caption element has - been popped from the stack. */ - while (true) { - $node = end($this->stack)->nodeName; - array_pop($this->stack); - - if ($node === 'caption') { - break; - } - } - - /* Clear the list of active formatting elements up to the last - marker. */ - $this->clearTheActiveFormattingElementsUpToTheLastMarker(); - - /* Switch the insertion mode to "in table". */ - $this->mode = self::IN_TABLE; - } - - /* A start tag whose tag name is one of: "caption", "col", "colgroup", - "tbody", "td", "tfoot", "th", "thead", "tr", or an end tag whose tag - name is "table" */ - } elseif (($token['type'] === HTML5::STARTTAG && in_array( - $token['name'], - array( - 'caption', - 'col', - 'colgroup', - 'tbody', - 'td', - 'tfoot', - 'th', - 'thead', - 'tr' - ) - )) || ($token['type'] === HTML5::ENDTAG && - $token['name'] === 'table') - ) { - /* Parse error. Act as if an end tag with the tag name "caption" - had been seen, then, if that token wasn't ignored, reprocess the - current token. */ - $this->inCaption( - array( - 'name' => 'caption', - 'type' => HTML5::ENDTAG - ) - ); - - return $this->inTable($token); - - /* An end tag whose tag name is one of: "body", "col", "colgroup", - "html", "tbody", "td", "tfoot", "th", "thead", "tr" */ - } elseif ($token['type'] === HTML5::ENDTAG && in_array( - $token['name'], - array( - 'body', - 'col', - 'colgroup', - 'html', - 'tbody', - 'tfoot', - 'th', - 'thead', - 'tr' - ) - ) - ) { - // Parse error. Ignore the token. - - /* Anything else */ - } else { - /* Process the token as if the insertion mode was "in body". */ - $this->inBody($token); - } - } - - private function inColumnGroup($token) - { - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - if ($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) - ) { - /* Append the character to the current node. */ - $text = $this->dom->createTextNode($token['data']); - end($this->stack)->appendChild($text); - - /* A comment token */ - } elseif ($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $comment = $this->dom->createComment($token['data']); - end($this->stack)->appendChild($comment); - - /* A start tag whose tag name is "col" */ - } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'col') { - /* Insert a col element for the token. Immediately pop the current - node off the stack of open elements. */ - $this->insertElement($token); - array_pop($this->stack); - - /* An end tag whose tag name is "colgroup" */ - } elseif ($token['type'] === HTML5::ENDTAG && - $token['name'] === 'colgroup' - ) { - /* If the current node is the root html element, then this is a - parse error, ignore the token. (innerHTML case) */ - if (end($this->stack)->nodeName === 'html') { - // Ignore - - /* Otherwise, pop the current node (which will be a colgroup - element) from the stack of open elements. Switch the insertion - mode to "in table". */ - } else { - array_pop($this->stack); - $this->mode = self::IN_TABLE; - } - - /* An end tag whose tag name is "col" */ - } elseif ($token['type'] === HTML5::ENDTAG && $token['name'] === 'col') { - /* Parse error. Ignore the token. */ - - /* Anything else */ - } else { - /* Act as if an end tag with the tag name "colgroup" had been seen, - and then, if that token wasn't ignored, reprocess the current token. */ - $this->inColumnGroup( - array( - 'name' => 'colgroup', - 'type' => HTML5::ENDTAG - ) - ); - - return $this->inTable($token); - } - } - - private function inTableBody($token) - { - $clear = array('tbody', 'tfoot', 'thead', 'html'); - - /* A start tag whose tag name is "tr" */ - if ($token['type'] === HTML5::STARTTAG && $token['name'] === 'tr') { - /* Clear the stack back to a table body context. */ - $this->clearStackToTableContext($clear); - - /* Insert a tr element for the token, then switch the insertion - mode to "in row". */ - $this->insertElement($token); - $this->mode = self::IN_ROW; - - /* A start tag whose tag name is one of: "th", "td" */ - } elseif ($token['type'] === HTML5::STARTTAG && - ($token['name'] === 'th' || $token['name'] === 'td') - ) { - /* Parse error. Act as if a start tag with the tag name "tr" had - been seen, then reprocess the current token. */ - $this->inTableBody( - array( - 'name' => 'tr', - 'type' => HTML5::STARTTAG, - 'attr' => array() - ) - ); - - return $this->inRow($token); - - /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */ - } elseif ($token['type'] === HTML5::ENDTAG && - in_array($token['name'], array('tbody', 'tfoot', 'thead')) - ) { - /* If the stack of open elements does not have an element in table - scope with the same tag name as the token, this is a parse error. - Ignore the token. */ - if (!$this->elementInScope($token['name'], true)) { - // Ignore - - /* Otherwise: */ - } else { - /* Clear the stack back to a table body context. */ - $this->clearStackToTableContext($clear); - - /* Pop the current node from the stack of open elements. Switch - the insertion mode to "in table". */ - array_pop($this->stack); - $this->mode = self::IN_TABLE; - } - - /* A start tag whose tag name is one of: "caption", "col", "colgroup", - "tbody", "tfoot", "thead", or an end tag whose tag name is "table" */ - } elseif (($token['type'] === HTML5::STARTTAG && in_array( - $token['name'], - array('caption', 'col', 'colgroup', 'tbody', 'tfoor', 'thead') - )) || - ($token['type'] === HTML5::STARTTAG && $token['name'] === 'table') - ) { - /* If the stack of open elements does not have a tbody, thead, or - tfoot element in table scope, this is a parse error. Ignore the - token. (innerHTML case) */ - if (!$this->elementInScope(array('tbody', 'thead', 'tfoot'), true)) { - // Ignore. - - /* Otherwise: */ - } else { - /* Clear the stack back to a table body context. */ - $this->clearStackToTableContext($clear); - - /* Act as if an end tag with the same tag name as the current - node ("tbody", "tfoot", or "thead") had been seen, then - reprocess the current token. */ - $this->inTableBody( - array( - 'name' => end($this->stack)->nodeName, - 'type' => HTML5::ENDTAG - ) - ); - - return $this->mainPhase($token); - } - - /* An end tag whose tag name is one of: "body", "caption", "col", - "colgroup", "html", "td", "th", "tr" */ - } elseif ($token['type'] === HTML5::ENDTAG && in_array( - $token['name'], - array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr') - ) - ) { - /* Parse error. Ignore the token. */ - - /* Anything else */ - } else { - /* Process the token as if the insertion mode was "in table". */ - $this->inTable($token); - } - } - - private function inRow($token) - { - $clear = array('tr', 'html'); - - /* A start tag whose tag name is one of: "th", "td" */ - if ($token['type'] === HTML5::STARTTAG && - ($token['name'] === 'th' || $token['name'] === 'td') - ) { - /* Clear the stack back to a table row context. */ - $this->clearStackToTableContext($clear); - - /* Insert an HTML element for the token, then switch the insertion - mode to "in cell". */ - $this->insertElement($token); - $this->mode = self::IN_CELL; - - /* Insert a marker at the end of the list of active formatting - elements. */ - $this->a_formatting[] = self::MARKER; - - /* An end tag whose tag name is "tr" */ - } elseif ($token['type'] === HTML5::ENDTAG && $token['name'] === 'tr') { - /* If the stack of open elements does not have an element in table - scope with the same tag name as the token, this is a parse error. - Ignore the token. (innerHTML case) */ - if (!$this->elementInScope($token['name'], true)) { - // Ignore. - - /* Otherwise: */ - } else { - /* Clear the stack back to a table row context. */ - $this->clearStackToTableContext($clear); - - /* Pop the current node (which will be a tr element) from the - stack of open elements. Switch the insertion mode to "in table - body". */ - array_pop($this->stack); - $this->mode = self::IN_TBODY; - } - - /* A start tag whose tag name is one of: "caption", "col", "colgroup", - "tbody", "tfoot", "thead", "tr" or an end tag whose tag name is "table" */ - } elseif ($token['type'] === HTML5::STARTTAG && in_array( - $token['name'], - array('caption', 'col', 'colgroup', 'tbody', 'tfoot', 'thead', 'tr') - ) - ) { - /* Act as if an end tag with the tag name "tr" had been seen, then, - if that token wasn't ignored, reprocess the current token. */ - $this->inRow( - array( - 'name' => 'tr', - 'type' => HTML5::ENDTAG - ) - ); - - return $this->inCell($token); - - /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */ - } elseif ($token['type'] === HTML5::ENDTAG && - in_array($token['name'], array('tbody', 'tfoot', 'thead')) - ) { - /* If the stack of open elements does not have an element in table - scope with the same tag name as the token, this is a parse error. - Ignore the token. */ - if (!$this->elementInScope($token['name'], true)) { - // Ignore. - - /* Otherwise: */ - } else { - /* Otherwise, act as if an end tag with the tag name "tr" had - been seen, then reprocess the current token. */ - $this->inRow( - array( - 'name' => 'tr', - 'type' => HTML5::ENDTAG - ) - ); - - return $this->inCell($token); - } - - /* An end tag whose tag name is one of: "body", "caption", "col", - "colgroup", "html", "td", "th" */ - } elseif ($token['type'] === HTML5::ENDTAG && in_array( - $token['name'], - array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr') - ) - ) { - /* Parse error. Ignore the token. */ - - /* Anything else */ - } else { - /* Process the token as if the insertion mode was "in table". */ - $this->inTable($token); - } - } - - private function inCell($token) - { - /* An end tag whose tag name is one of: "td", "th" */ - if ($token['type'] === HTML5::ENDTAG && - ($token['name'] === 'td' || $token['name'] === 'th') - ) { - /* If the stack of open elements does not have an element in table - scope with the same tag name as that of the token, then this is a - parse error and the token must be ignored. */ - if (!$this->elementInScope($token['name'], true)) { - // Ignore. - - /* Otherwise: */ - } else { - /* Generate implied end tags, except for elements with the same - tag name as the token. */ - $this->generateImpliedEndTags(array($token['name'])); - - /* Now, if the current node is not an element with the same tag - name as the token, then this is a parse error. */ - // k - - /* Pop elements from this stack until an element with the same - tag name as the token has been popped from the stack. */ - while (true) { - $node = end($this->stack)->nodeName; - array_pop($this->stack); - - if ($node === $token['name']) { - break; - } - } - - /* Clear the list of active formatting elements up to the last - marker. */ - $this->clearTheActiveFormattingElementsUpToTheLastMarker(); - - /* Switch the insertion mode to "in row". (The current node - will be a tr element at this point.) */ - $this->mode = self::IN_ROW; - } - - /* A start tag whose tag name is one of: "caption", "col", "colgroup", - "tbody", "td", "tfoot", "th", "thead", "tr" */ - } elseif ($token['type'] === HTML5::STARTTAG && in_array( - $token['name'], - array( - 'caption', - 'col', - 'colgroup', - 'tbody', - 'td', - 'tfoot', - 'th', - 'thead', - 'tr' - ) - ) - ) { - /* If the stack of open elements does not have a td or th element - in table scope, then this is a parse error; ignore the token. - (innerHTML case) */ - if (!$this->elementInScope(array('td', 'th'), true)) { - // Ignore. - - /* Otherwise, close the cell (see below) and reprocess the current - token. */ - } else { - $this->closeCell(); - return $this->inRow($token); - } - - /* A start tag whose tag name is one of: "caption", "col", "colgroup", - "tbody", "td", "tfoot", "th", "thead", "tr" */ - } elseif ($token['type'] === HTML5::STARTTAG && in_array( - $token['name'], - array( - 'caption', - 'col', - 'colgroup', - 'tbody', - 'td', - 'tfoot', - 'th', - 'thead', - 'tr' - ) - ) - ) { - /* If the stack of open elements does not have a td or th element - in table scope, then this is a parse error; ignore the token. - (innerHTML case) */ - if (!$this->elementInScope(array('td', 'th'), true)) { - // Ignore. - - /* Otherwise, close the cell (see below) and reprocess the current - token. */ - } else { - $this->closeCell(); - return $this->inRow($token); - } - - /* An end tag whose tag name is one of: "body", "caption", "col", - "colgroup", "html" */ - } elseif ($token['type'] === HTML5::ENDTAG && in_array( - $token['name'], - array('body', 'caption', 'col', 'colgroup', 'html') - ) - ) { - /* Parse error. Ignore the token. */ - - /* An end tag whose tag name is one of: "table", "tbody", "tfoot", - "thead", "tr" */ - } elseif ($token['type'] === HTML5::ENDTAG && in_array( - $token['name'], - array('table', 'tbody', 'tfoot', 'thead', 'tr') - ) - ) { - /* If the stack of open elements does not have an element in table - scope with the same tag name as that of the token (which can only - happen for "tbody", "tfoot" and "thead", or, in the innerHTML case), - then this is a parse error and the token must be ignored. */ - if (!$this->elementInScope($token['name'], true)) { - // Ignore. - - /* Otherwise, close the cell (see below) and reprocess the current - token. */ - } else { - $this->closeCell(); - return $this->inRow($token); - } - - /* Anything else */ - } else { - /* Process the token as if the insertion mode was "in body". */ - $this->inBody($token); - } - } - - private function inSelect($token) - { - /* Handle the token as follows: */ - - /* A character token */ - if ($token['type'] === HTML5::CHARACTR) { - /* Append the token's character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif ($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $this->insertComment($token['data']); - - /* A start tag token whose tag name is "option" */ - } elseif ($token['type'] === HTML5::STARTTAG && - $token['name'] === 'option' - ) { - /* If the current node is an option element, act as if an end tag - with the tag name "option" had been seen. */ - if (end($this->stack)->nodeName === 'option') { - $this->inSelect( - array( - 'name' => 'option', - 'type' => HTML5::ENDTAG - ) - ); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* A start tag token whose tag name is "optgroup" */ - } elseif ($token['type'] === HTML5::STARTTAG && - $token['name'] === 'optgroup' - ) { - /* If the current node is an option element, act as if an end tag - with the tag name "option" had been seen. */ - if (end($this->stack)->nodeName === 'option') { - $this->inSelect( - array( - 'name' => 'option', - 'type' => HTML5::ENDTAG - ) - ); - } - - /* If the current node is an optgroup element, act as if an end tag - with the tag name "optgroup" had been seen. */ - if (end($this->stack)->nodeName === 'optgroup') { - $this->inSelect( - array( - 'name' => 'optgroup', - 'type' => HTML5::ENDTAG - ) - ); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* An end tag token whose tag name is "optgroup" */ - } elseif ($token['type'] === HTML5::ENDTAG && - $token['name'] === 'optgroup' - ) { - /* First, if the current node is an option element, and the node - immediately before it in the stack of open elements is an optgroup - element, then act as if an end tag with the tag name "option" had - been seen. */ - $elements_in_stack = count($this->stack); - - if ($this->stack[$elements_in_stack - 1]->nodeName === 'option' && - $this->stack[$elements_in_stack - 2]->nodeName === 'optgroup' - ) { - $this->inSelect( - array( - 'name' => 'option', - 'type' => HTML5::ENDTAG - ) - ); - } - - /* If the current node is an optgroup element, then pop that node - from the stack of open elements. Otherwise, this is a parse error, - ignore the token. */ - if ($this->stack[$elements_in_stack - 1] === 'optgroup') { - array_pop($this->stack); - } - - /* An end tag token whose tag name is "option" */ - } elseif ($token['type'] === HTML5::ENDTAG && - $token['name'] === 'option' - ) { - /* If the current node is an option element, then pop that node - from the stack of open elements. Otherwise, this is a parse error, - ignore the token. */ - if (end($this->stack)->nodeName === 'option') { - array_pop($this->stack); - } - - /* An end tag whose tag name is "select" */ - } elseif ($token['type'] === HTML5::ENDTAG && - $token['name'] === 'select' - ) { - /* If the stack of open elements does not have an element in table - scope with the same tag name as the token, this is a parse error. - Ignore the token. (innerHTML case) */ - if (!$this->elementInScope($token['name'], true)) { - // w/e - - /* Otherwise: */ - } else { - /* Pop elements from the stack of open elements until a select - element has been popped from the stack. */ - while (true) { - $current = end($this->stack)->nodeName; - array_pop($this->stack); - - if ($current === 'select') { - break; - } - } - - /* Reset the insertion mode appropriately. */ - $this->resetInsertionMode(); - } - - /* A start tag whose tag name is "select" */ - } elseif ($token['name'] === 'select' && - $token['type'] === HTML5::STARTTAG - ) { - /* Parse error. Act as if the token had been an end tag with the - tag name "select" instead. */ - $this->inSelect( - array( - 'name' => 'select', - 'type' => HTML5::ENDTAG - ) - ); - - /* An end tag whose tag name is one of: "caption", "table", "tbody", - "tfoot", "thead", "tr", "td", "th" */ - } elseif (in_array( - $token['name'], - array( - 'caption', - 'table', - 'tbody', - 'tfoot', - 'thead', - 'tr', - 'td', - 'th' - ) - ) && $token['type'] === HTML5::ENDTAG - ) { - /* Parse error. */ - // w/e - - /* If the stack of open elements has an element in table scope with - the same tag name as that of the token, then act as if an end tag - with the tag name "select" had been seen, and reprocess the token. - Otherwise, ignore the token. */ - if ($this->elementInScope($token['name'], true)) { - $this->inSelect( - array( - 'name' => 'select', - 'type' => HTML5::ENDTAG - ) - ); - - $this->mainPhase($token); - } - - /* Anything else */ - } else { - /* Parse error. Ignore the token. */ - } - } - - private function afterBody($token) - { - /* Handle the token as follows: */ - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - if ($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) - ) { - /* Process the token as it would be processed if the insertion mode - was "in body". */ - $this->inBody($token); - - /* A comment token */ - } elseif ($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the first element in the stack of open - elements (the html element), with the data attribute set to the - data given in the comment token. */ - $comment = $this->dom->createComment($token['data']); - $this->stack[0]->appendChild($comment); - - /* An end tag with the tag name "html" */ - } elseif ($token['type'] === HTML5::ENDTAG && $token['name'] === 'html') { - /* If the parser was originally created in order to handle the - setting of an element's innerHTML attribute, this is a parse error; - ignore the token. (The element will be an html element in this - case.) (innerHTML case) */ - - /* Otherwise, switch to the trailing end phase. */ - $this->phase = self::END_PHASE; - - /* Anything else */ - } else { - /* Parse error. Set the insertion mode to "in body" and reprocess - the token. */ - $this->mode = self::IN_BODY; - return $this->inBody($token); - } - } - - private function inFrameset($token) - { - /* Handle the token as follows: */ - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */ - if ($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) - ) { - /* Append the character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif ($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $this->insertComment($token['data']); - - /* A start tag with the tag name "frameset" */ - } elseif ($token['name'] === 'frameset' && - $token['type'] === HTML5::STARTTAG - ) { - $this->insertElement($token); - - /* An end tag with the tag name "frameset" */ - } elseif ($token['name'] === 'frameset' && - $token['type'] === HTML5::ENDTAG - ) { - /* If the current node is the root html element, then this is a - parse error; ignore the token. (innerHTML case) */ - if (end($this->stack)->nodeName === 'html') { - // Ignore - - } else { - /* Otherwise, pop the current node from the stack of open - elements. */ - array_pop($this->stack); - - /* If the parser was not originally created in order to handle - the setting of an element's innerHTML attribute (innerHTML case), - and the current node is no longer a frameset element, then change - the insertion mode to "after frameset". */ - $this->mode = self::AFTR_FRAME; - } - - /* A start tag with the tag name "frame" */ - } elseif ($token['name'] === 'frame' && - $token['type'] === HTML5::STARTTAG - ) { - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Immediately pop the current node off the stack of open elements. */ - array_pop($this->stack); - - /* A start tag with the tag name "noframes" */ - } elseif ($token['name'] === 'noframes' && - $token['type'] === HTML5::STARTTAG - ) { - /* Process the token as if the insertion mode had been "in body". */ - $this->inBody($token); - - /* Anything else */ - } else { - /* Parse error. Ignore the token. */ - } - } - - private function afterFrameset($token) - { - /* Handle the token as follows: */ - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */ - if ($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) - ) { - /* Append the character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif ($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $this->insertComment($token['data']); - - /* An end tag with the tag name "html" */ - } elseif ($token['name'] === 'html' && - $token['type'] === HTML5::ENDTAG - ) { - /* Switch to the trailing end phase. */ - $this->phase = self::END_PHASE; - - /* A start tag with the tag name "noframes" */ - } elseif ($token['name'] === 'noframes' && - $token['type'] === HTML5::STARTTAG - ) { - /* Process the token as if the insertion mode had been "in body". */ - $this->inBody($token); - - /* Anything else */ - } else { - /* Parse error. Ignore the token. */ - } - } - - private function trailingEndPhase($token) - { - /* After the main phase, as each token is emitted from the tokenisation - stage, it must be processed as described in this section. */ - - /* A DOCTYPE token */ - if ($token['type'] === HTML5::DOCTYPE) { - // Parse error. Ignore the token. - - /* A comment token */ - } elseif ($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the Document object with the data - attribute set to the data given in the comment token. */ - $comment = $this->dom->createComment($token['data']); - $this->dom->appendChild($comment); - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - } elseif ($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) - ) { - /* Process the token as it would be processed in the main phase. */ - $this->mainPhase($token); - - /* A character token that is not one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE. Or a start tag token. Or an end tag token. */ - } elseif (($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) || - $token['type'] === HTML5::STARTTAG || $token['type'] === HTML5::ENDTAG - ) { - /* Parse error. Switch back to the main phase and reprocess the - token. */ - $this->phase = self::MAIN_PHASE; - return $this->mainPhase($token); - - /* An end-of-file token */ - } elseif ($token['type'] === HTML5::EOF) { - /* OMG DONE!! */ - } - } - - private function insertElement($token, $append = true, $check = false) - { - // Proprietary workaround for libxml2's limitations with tag names - if ($check) { - // Slightly modified HTML5 tag-name modification, - // removing anything that's not an ASCII letter, digit, or hyphen - $token['name'] = preg_replace('/[^a-z0-9-]/i', '', $token['name']); - // Remove leading hyphens and numbers - $token['name'] = ltrim($token['name'], '-0..9'); - // In theory, this should ever be needed, but just in case - if ($token['name'] === '') { - $token['name'] = 'span'; - } // arbitrary generic choice - } - - $el = $this->dom->createElement($token['name']); - - foreach ($token['attr'] as $attr) { - if (!$el->hasAttribute($attr['name'])) { - $el->setAttribute($attr['name'], (string)$attr['value']); - } - } - - $this->appendToRealParent($el); - $this->stack[] = $el; - - return $el; - } - - private function insertText($data) - { - $text = $this->dom->createTextNode($data); - $this->appendToRealParent($text); - } - - private function insertComment($data) - { - $comment = $this->dom->createComment($data); - $this->appendToRealParent($comment); - } - - private function appendToRealParent($node) - { - if ($this->foster_parent === null) { - end($this->stack)->appendChild($node); - - } elseif ($this->foster_parent !== null) { - /* If the foster parent element is the parent element of the - last table element in the stack of open elements, then the new - node must be inserted immediately before the last table element - in the stack of open elements in the foster parent element; - otherwise, the new node must be appended to the foster parent - element. */ - for ($n = count($this->stack) - 1; $n >= 0; $n--) { - if ($this->stack[$n]->nodeName === 'table' && - $this->stack[$n]->parentNode !== null - ) { - $table = $this->stack[$n]; - break; - } - } - - if (isset($table) && $this->foster_parent->isSameNode($table->parentNode)) { - $this->foster_parent->insertBefore($node, $table); - } else { - $this->foster_parent->appendChild($node); - } - - $this->foster_parent = null; - } - } - - private function elementInScope($el, $table = false) - { - if (is_array($el)) { - foreach ($el as $element) { - if ($this->elementInScope($element, $table)) { - return true; - } - } - - return false; - } - - $leng = count($this->stack); - - for ($n = 0; $n < $leng; $n++) { - /* 1. Initialise node to be the current node (the bottommost node of - the stack). */ - $node = $this->stack[$leng - 1 - $n]; - - if ($node->tagName === $el) { - /* 2. If node is the target node, terminate in a match state. */ - return true; - - } elseif ($node->tagName === 'table') { - /* 3. Otherwise, if node is a table element, terminate in a failure - state. */ - return false; - - } elseif ($table === true && in_array( - $node->tagName, - array( - 'caption', - 'td', - 'th', - 'button', - 'marquee', - 'object' - ) - ) - ) { - /* 4. Otherwise, if the algorithm is the "has an element in scope" - variant (rather than the "has an element in table scope" variant), - and node is one of the following, terminate in a failure state. */ - return false; - - } elseif ($node === $node->ownerDocument->documentElement) { - /* 5. Otherwise, if node is an html element (root element), terminate - in a failure state. (This can only happen if the node is the topmost - node of the stack of open elements, and prevents the next step from - being invoked if there are no more elements in the stack.) */ - return false; - } - - /* Otherwise, set node to the previous entry in the stack of open - elements and return to step 2. (This will never fail, since the loop - will always terminate in the previous step if the top of the stack - is reached.) */ - } - } - - private function reconstructActiveFormattingElements() - { - /* 1. If there are no entries in the list of active formatting elements, - then there is nothing to reconstruct; stop this algorithm. */ - $formatting_elements = count($this->a_formatting); - - if ($formatting_elements === 0) { - return false; - } - - /* 3. Let entry be the last (most recently added) element in the list - of active formatting elements. */ - $entry = end($this->a_formatting); - - /* 2. If the last (most recently added) entry in the list of active - formatting elements is a marker, or if it is an element that is in the - stack of open elements, then there is nothing to reconstruct; stop this - algorithm. */ - if ($entry === self::MARKER || in_array($entry, $this->stack, true)) { - return false; - } - - for ($a = $formatting_elements - 1; $a >= 0; true) { - /* 4. If there are no entries before entry in the list of active - formatting elements, then jump to step 8. */ - if ($a === 0) { - $step_seven = false; - break; - } - - /* 5. Let entry be the entry one earlier than entry in the list of - active formatting elements. */ - $a--; - $entry = $this->a_formatting[$a]; - - /* 6. If entry is neither a marker nor an element that is also in - thetack of open elements, go to step 4. */ - if ($entry === self::MARKER || in_array($entry, $this->stack, true)) { - break; - } - } - - while (true) { - /* 7. Let entry be the element one later than entry in the list of - active formatting elements. */ - if (isset($step_seven) && $step_seven === true) { - $a++; - $entry = $this->a_formatting[$a]; - } - - /* 8. Perform a shallow clone of the element entry to obtain clone. */ - $clone = $entry->cloneNode(); - - /* 9. Append clone to the current node and push it onto the stack - of open elements so that it is the new current node. */ - end($this->stack)->appendChild($clone); - $this->stack[] = $clone; - - /* 10. Replace the entry for entry in the list with an entry for - clone. */ - $this->a_formatting[$a] = $clone; - - /* 11. If the entry for clone in the list of active formatting - elements is not the last entry in the list, return to step 7. */ - if (end($this->a_formatting) !== $clone) { - $step_seven = true; - } else { - break; - } - } - } - - private function clearTheActiveFormattingElementsUpToTheLastMarker() - { - /* When the steps below require the UA to clear the list of active - formatting elements up to the last marker, the UA must perform the - following steps: */ - - while (true) { - /* 1. Let entry be the last (most recently added) entry in the list - of active formatting elements. */ - $entry = end($this->a_formatting); - - /* 2. Remove entry from the list of active formatting elements. */ - array_pop($this->a_formatting); - - /* 3. If entry was a marker, then stop the algorithm at this point. - The list has been cleared up to the last marker. */ - if ($entry === self::MARKER) { - break; - } - } - } - - private function generateImpliedEndTags($exclude = array()) - { - /* When the steps below require the UA to generate implied end tags, - then, if the current node is a dd element, a dt element, an li element, - a p element, a td element, a th element, or a tr element, the UA must - act as if an end tag with the respective tag name had been seen and - then generate implied end tags again. */ - $node = end($this->stack); - $elements = array_diff(array('dd', 'dt', 'li', 'p', 'td', 'th', 'tr'), $exclude); - - while (in_array(end($this->stack)->nodeName, $elements)) { - array_pop($this->stack); - } - } - - private function getElementCategory($node) - { - $name = $node->tagName; - if (in_array($name, $this->special)) { - return self::SPECIAL; - } elseif (in_array($name, $this->scoping)) { - return self::SCOPING; - } elseif (in_array($name, $this->formatting)) { - return self::FORMATTING; - } else { - return self::PHRASING; - } - } - - private function clearStackToTableContext($elements) - { - /* When the steps above require the UA to clear the stack back to a - table context, it means that the UA must, while the current node is not - a table element or an html element, pop elements from the stack of open - elements. If this causes any elements to be popped from the stack, then - this is a parse error. */ - while (true) { - $node = end($this->stack)->nodeName; - - if (in_array($node, $elements)) { - break; - } else { - array_pop($this->stack); - } - } - } - - private function resetInsertionMode() - { - /* 1. Let last be false. */ - $last = false; - $leng = count($this->stack); - - for ($n = $leng - 1; $n >= 0; $n--) { - /* 2. Let node be the last node in the stack of open elements. */ - $node = $this->stack[$n]; - - /* 3. If node is the first node in the stack of open elements, then - set last to true. If the element whose innerHTML attribute is being - set is neither a td element nor a th element, then set node to the - element whose innerHTML attribute is being set. (innerHTML case) */ - if ($this->stack[0]->isSameNode($node)) { - $last = true; - } - - /* 4. If node is a select element, then switch the insertion mode to - "in select" and abort these steps. (innerHTML case) */ - if ($node->nodeName === 'select') { - $this->mode = self::IN_SELECT; - break; - - /* 5. If node is a td or th element, then switch the insertion mode - to "in cell" and abort these steps. */ - } elseif ($node->nodeName === 'td' || $node->nodeName === 'th') { - $this->mode = self::IN_CELL; - break; - - /* 6. If node is a tr element, then switch the insertion mode to - "in row" and abort these steps. */ - } elseif ($node->nodeName === 'tr') { - $this->mode = self::IN_ROW; - break; - - /* 7. If node is a tbody, thead, or tfoot element, then switch the - insertion mode to "in table body" and abort these steps. */ - } elseif (in_array($node->nodeName, array('tbody', 'thead', 'tfoot'))) { - $this->mode = self::IN_TBODY; - break; - - /* 8. If node is a caption element, then switch the insertion mode - to "in caption" and abort these steps. */ - } elseif ($node->nodeName === 'caption') { - $this->mode = self::IN_CAPTION; - break; - - /* 9. If node is a colgroup element, then switch the insertion mode - to "in column group" and abort these steps. (innerHTML case) */ - } elseif ($node->nodeName === 'colgroup') { - $this->mode = self::IN_CGROUP; - break; - - /* 10. If node is a table element, then switch the insertion mode - to "in table" and abort these steps. */ - } elseif ($node->nodeName === 'table') { - $this->mode = self::IN_TABLE; - break; - - /* 11. If node is a head element, then switch the insertion mode - to "in body" ("in body"! not "in head"!) and abort these steps. - (innerHTML case) */ - } elseif ($node->nodeName === 'head') { - $this->mode = self::IN_BODY; - break; - - /* 12. If node is a body element, then switch the insertion mode to - "in body" and abort these steps. */ - } elseif ($node->nodeName === 'body') { - $this->mode = self::IN_BODY; - break; - - /* 13. If node is a frameset element, then switch the insertion - mode to "in frameset" and abort these steps. (innerHTML case) */ - } elseif ($node->nodeName === 'frameset') { - $this->mode = self::IN_FRAME; - break; - - /* 14. If node is an html element, then: if the head element - pointer is null, switch the insertion mode to "before head", - otherwise, switch the insertion mode to "after head". In either - case, abort these steps. (innerHTML case) */ - } elseif ($node->nodeName === 'html') { - $this->mode = ($this->head_pointer === null) - ? self::BEFOR_HEAD - : self::AFTER_HEAD; - - break; - - /* 15. If last is true, then set the insertion mode to "in body" - and abort these steps. (innerHTML case) */ - } elseif ($last) { - $this->mode = self::IN_BODY; - break; - } - } - } - - private function closeCell() - { - /* If the stack of open elements has a td or th element in table scope, - then act as if an end tag token with that tag name had been seen. */ - foreach (array('td', 'th') as $cell) { - if ($this->elementInScope($cell, true)) { - $this->inCell( - array( - 'name' => $cell, - 'type' => HTML5::ENDTAG - ) - ); - - break; - } - } - } - - public function save() - { - return $this->dom; - } -} diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node.php deleted file mode 100644 index 3995fec9f..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node.php +++ /dev/null @@ -1,49 +0,0 @@ -data = $data; - $this->line = $line; - $this->col = $col; - } - - public function toTokenPair() { - return array(new HTMLPurifier_Token_Comment($this->data, $this->line, $this->col), null); - } -} diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node/Element.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node/Element.php deleted file mode 100644 index 6cbf56dad..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node/Element.php +++ /dev/null @@ -1,59 +0,0 @@ - form or the form, i.e. - * is it a pair of start/end tokens or an empty token. - * @bool - */ - public $empty = false; - - public $endCol = null, $endLine = null, $endArmor = array(); - - public function __construct($name, $attr = array(), $line = null, $col = null, $armor = array()) { - $this->name = $name; - $this->attr = $attr; - $this->line = $line; - $this->col = $col; - $this->armor = $armor; - } - - public function toTokenPair() { - // XXX inefficiency here, normalization is not necessary - if ($this->empty) { - return array(new HTMLPurifier_Token_Empty($this->name, $this->attr, $this->line, $this->col, $this->armor), null); - } else { - $start = new HTMLPurifier_Token_Start($this->name, $this->attr, $this->line, $this->col, $this->armor); - $end = new HTMLPurifier_Token_End($this->name, array(), $this->endLine, $this->endCol, $this->endArmor); - //$end->start = $start; - return array($start, $end); - } - } -} - diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node/Text.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node/Text.php deleted file mode 100644 index aec916647..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node/Text.php +++ /dev/null @@ -1,54 +0,0 @@ -data = $data; - $this->is_whitespace = $is_whitespace; - $this->line = $line; - $this->col = $col; - } - - public function toTokenPair() { - return array(new HTMLPurifier_Token_Text($this->data, $this->line, $this->col), null); - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PercentEncoder.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PercentEncoder.php deleted file mode 100644 index 18c8bbb00..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PercentEncoder.php +++ /dev/null @@ -1,111 +0,0 @@ -preserve[$i] = true; - } - for ($i = 65; $i <= 90; $i++) { // upper-case - $this->preserve[$i] = true; - } - for ($i = 97; $i <= 122; $i++) { // lower-case - $this->preserve[$i] = true; - } - $this->preserve[45] = true; // Dash - - $this->preserve[46] = true; // Period . - $this->preserve[95] = true; // Underscore _ - $this->preserve[126]= true; // Tilde ~ - - // extra letters not to escape - if ($preserve !== false) { - for ($i = 0, $c = strlen($preserve); $i < $c; $i++) { - $this->preserve[ord($preserve[$i])] = true; - } - } - } - - /** - * Our replacement for urlencode, it encodes all non-reserved characters, - * as well as any extra characters that were instructed to be preserved. - * @note - * Assumes that the string has already been normalized, making any - * and all percent escape sequences valid. Percents will not be - * re-escaped, regardless of their status in $preserve - * @param string $string String to be encoded - * @return string Encoded string. - */ - public function encode($string) - { - $ret = ''; - for ($i = 0, $c = strlen($string); $i < $c; $i++) { - if ($string[$i] !== '%' && !isset($this->preserve[$int = ord($string[$i])])) { - $ret .= '%' . sprintf('%02X', $int); - } else { - $ret .= $string[$i]; - } - } - return $ret; - } - - /** - * Fix up percent-encoding by decoding unreserved characters and normalizing. - * @warning This function is affected by $preserve, even though the - * usual desired behavior is for this not to preserve those - * characters. Be careful when reusing instances of PercentEncoder! - * @param string $string String to normalize - * @return string - */ - public function normalize($string) - { - if ($string == '') { - return ''; - } - $parts = explode('%', $string); - $ret = array_shift($parts); - foreach ($parts as $part) { - $length = strlen($part); - if ($length < 2) { - $ret .= '%25' . $part; - continue; - } - $encoding = substr($part, 0, 2); - $text = substr($part, 2); - if (!ctype_xdigit($encoding)) { - $ret .= '%25' . $part; - continue; - } - $int = hexdec($encoding); - if (isset($this->preserve[$int])) { - $ret .= chr($int) . $text; - continue; - } - $encoding = strtoupper($encoding); - $ret .= '%' . $encoding . $text; - } - return $ret; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer.php deleted file mode 100644 index 549e4cea1..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer.php +++ /dev/null @@ -1,218 +0,0 @@ -getAll(); - $context = new HTMLPurifier_Context(); - $this->generator = new HTMLPurifier_Generator($config, $context); - } - - /** - * Main function that renders object or aspect of that object - * @note Parameters vary depending on printer - */ - // function render() {} - - /** - * Returns a start tag - * @param string $tag Tag name - * @param array $attr Attribute array - * @return string - */ - protected function start($tag, $attr = array()) - { - return $this->generator->generateFromToken( - new HTMLPurifier_Token_Start($tag, $attr ? $attr : array()) - ); - } - - /** - * Returns an end tag - * @param string $tag Tag name - * @return string - */ - protected function end($tag) - { - return $this->generator->generateFromToken( - new HTMLPurifier_Token_End($tag) - ); - } - - /** - * Prints a complete element with content inside - * @param string $tag Tag name - * @param string $contents Element contents - * @param array $attr Tag attributes - * @param bool $escape whether or not to escape contents - * @return string - */ - protected function element($tag, $contents, $attr = array(), $escape = true) - { - return $this->start($tag, $attr) . - ($escape ? $this->escape($contents) : $contents) . - $this->end($tag); - } - - /** - * @param string $tag - * @param array $attr - * @return string - */ - protected function elementEmpty($tag, $attr = array()) - { - return $this->generator->generateFromToken( - new HTMLPurifier_Token_Empty($tag, $attr) - ); - } - - /** - * @param string $text - * @return string - */ - protected function text($text) - { - return $this->generator->generateFromToken( - new HTMLPurifier_Token_Text($text) - ); - } - - /** - * Prints a simple key/value row in a table. - * @param string $name Key - * @param mixed $value Value - * @return string - */ - protected function row($name, $value) - { - if (is_bool($value)) { - $value = $value ? 'On' : 'Off'; - } - return - $this->start('tr') . "\n" . - $this->element('th', $name) . "\n" . - $this->element('td', $value) . "\n" . - $this->end('tr'); - } - - /** - * Escapes a string for HTML output. - * @param string $string String to escape - * @return string - */ - protected function escape($string) - { - $string = HTMLPurifier_Encoder::cleanUTF8($string); - $string = htmlspecialchars($string, ENT_COMPAT, 'UTF-8'); - return $string; - } - - /** - * Takes a list of strings and turns them into a single list - * @param string[] $array List of strings - * @param bool $polite Bool whether or not to add an end before the last - * @return string - */ - protected function listify($array, $polite = false) - { - if (empty($array)) { - return 'None'; - } - $ret = ''; - $i = count($array); - foreach ($array as $value) { - $i--; - $ret .= $value; - if ($i > 0 && !($polite && $i == 1)) { - $ret .= ', '; - } - if ($polite && $i == 1) { - $ret .= 'and '; - } - } - return $ret; - } - - /** - * Retrieves the class of an object without prefixes, as well as metadata - * @param object $obj Object to determine class of - * @param string $sec_prefix Further prefix to remove - * @return string - */ - protected function getClass($obj, $sec_prefix = '') - { - static $five = null; - if ($five === null) { - $five = version_compare(PHP_VERSION, '5', '>='); - } - $prefix = 'HTMLPurifier_' . $sec_prefix; - if (!$five) { - $prefix = strtolower($prefix); - } - $class = str_replace($prefix, '', get_class($obj)); - $lclass = strtolower($class); - $class .= '('; - switch ($lclass) { - case 'enum': - $values = array(); - foreach ($obj->valid_values as $value => $bool) { - $values[] = $value; - } - $class .= implode(', ', $values); - break; - case 'css_composite': - $values = array(); - foreach ($obj->defs as $def) { - $values[] = $this->getClass($def, $sec_prefix); - } - $class .= implode(', ', $values); - break; - case 'css_multiple': - $class .= $this->getClass($obj->single, $sec_prefix) . ', '; - $class .= $obj->max; - break; - case 'css_denyelementdecorator': - $class .= $this->getClass($obj->def, $sec_prefix) . ', '; - $class .= $obj->element; - break; - case 'css_importantdecorator': - $class .= $this->getClass($obj->def, $sec_prefix); - if ($obj->allow) { - $class .= ', !important'; - } - break; - } - $class .= ')'; - return $class; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php deleted file mode 100644 index 29505fe12..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php +++ /dev/null @@ -1,44 +0,0 @@ -def = $config->getCSSDefinition(); - $ret = ''; - - $ret .= $this->start('div', array('class' => 'HTMLPurifier_Printer')); - $ret .= $this->start('table'); - - $ret .= $this->element('caption', 'Properties ($info)'); - - $ret .= $this->start('thead'); - $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Property', array('class' => 'heavy')); - $ret .= $this->element('th', 'Definition', array('class' => 'heavy', 'style' => 'width:auto;')); - $ret .= $this->end('tr'); - $ret .= $this->end('thead'); - - ksort($this->def->info); - foreach ($this->def->info as $property => $obj) { - $name = $this->getClass($obj, 'AttrDef_'); - $ret .= $this->row($property, $name); - } - - $ret .= $this->end('table'); - $ret .= $this->end('div'); - - return $ret; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.css b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.css deleted file mode 100644 index 3ff1a88aa..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.css +++ /dev/null @@ -1,10 +0,0 @@ - -.hp-config {} - -.hp-config tbody th {text-align:right; padding-right:0.5em;} -.hp-config thead, .hp-config .namespace {background:#3C578C; color:#FFF;} -.hp-config .namespace th {text-align:center;} -.hp-config .verbose {display:none;} -.hp-config .controls {text-align:center;} - -/* vim: et sw=4 sts=4 */ diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.js b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.js deleted file mode 100644 index cba00c9b8..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.js +++ /dev/null @@ -1,5 +0,0 @@ -function toggleWriteability(id_of_patient, checked) { - document.getElementById(id_of_patient).disabled = checked; -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php deleted file mode 100644 index 33ae11397..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php +++ /dev/null @@ -1,451 +0,0 @@ -docURL = $doc_url; - $this->name = $name; - $this->compress = $compress; - // initialize sub-printers - $this->fields[0] = new HTMLPurifier_Printer_ConfigForm_default(); - $this->fields[HTMLPurifier_VarParser::C_BOOL] = new HTMLPurifier_Printer_ConfigForm_bool(); - } - - /** - * Sets default column and row size for textareas in sub-printers - * @param $cols Integer columns of textarea, null to use default - * @param $rows Integer rows of textarea, null to use default - */ - public function setTextareaDimensions($cols = null, $rows = null) - { - if ($cols) { - $this->fields['default']->cols = $cols; - } - if ($rows) { - $this->fields['default']->rows = $rows; - } - } - - /** - * Retrieves styling, in case it is not accessible by webserver - */ - public static function getCSS() - { - return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.css'); - } - - /** - * Retrieves JavaScript, in case it is not accessible by webserver - */ - public static function getJavaScript() - { - return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.js'); - } - - /** - * Returns HTML output for a configuration form - * @param HTMLPurifier_Config|array $config Configuration object of current form state, or an array - * where [0] has an HTML namespace and [1] is being rendered. - * @param array|bool $allowed Optional namespace(s) and directives to restrict form to. - * @param bool $render_controls - * @return string - */ - public function render($config, $allowed = true, $render_controls = true) - { - if (is_array($config) && isset($config[0])) { - $gen_config = $config[0]; - $config = $config[1]; - } else { - $gen_config = $config; - } - - $this->config = $config; - $this->genConfig = $gen_config; - $this->prepareGenerator($gen_config); - - $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $config->def); - $all = array(); - foreach ($allowed as $key) { - list($ns, $directive) = $key; - $all[$ns][$directive] = $config->get($ns . '.' . $directive); - } - - $ret = ''; - $ret .= $this->start('table', array('class' => 'hp-config')); - $ret .= $this->start('thead'); - $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Directive', array('class' => 'hp-directive')); - $ret .= $this->element('th', 'Value', array('class' => 'hp-value')); - $ret .= $this->end('tr'); - $ret .= $this->end('thead'); - foreach ($all as $ns => $directives) { - $ret .= $this->renderNamespace($ns, $directives); - } - if ($render_controls) { - $ret .= $this->start('tbody'); - $ret .= $this->start('tr'); - $ret .= $this->start('td', array('colspan' => 2, 'class' => 'controls')); - $ret .= $this->elementEmpty('input', array('type' => 'submit', 'value' => 'Submit')); - $ret .= '[Reset]'; - $ret .= $this->end('td'); - $ret .= $this->end('tr'); - $ret .= $this->end('tbody'); - } - $ret .= $this->end('table'); - return $ret; - } - - /** - * Renders a single namespace - * @param $ns String namespace name - * @param array $directives array of directives to values - * @return string - */ - protected function renderNamespace($ns, $directives) - { - $ret = ''; - $ret .= $this->start('tbody', array('class' => 'namespace')); - $ret .= $this->start('tr'); - $ret .= $this->element('th', $ns, array('colspan' => 2)); - $ret .= $this->end('tr'); - $ret .= $this->end('tbody'); - $ret .= $this->start('tbody'); - foreach ($directives as $directive => $value) { - $ret .= $this->start('tr'); - $ret .= $this->start('th'); - if ($this->docURL) { - $url = str_replace('%s', urlencode("$ns.$directive"), $this->docURL); - $ret .= $this->start('a', array('href' => $url)); - } - $attr = array('for' => "{$this->name}:$ns.$directive"); - - // crop directive name if it's too long - if (!$this->compress || (strlen($directive) < $this->compress)) { - $directive_disp = $directive; - } else { - $directive_disp = substr($directive, 0, $this->compress - 2) . '...'; - $attr['title'] = $directive; - } - - $ret .= $this->element( - 'label', - $directive_disp, - // component printers must create an element with this id - $attr - ); - if ($this->docURL) { - $ret .= $this->end('a'); - } - $ret .= $this->end('th'); - - $ret .= $this->start('td'); - $def = $this->config->def->info["$ns.$directive"]; - if (is_int($def)) { - $allow_null = $def < 0; - $type = abs($def); - } else { - $type = $def->type; - $allow_null = isset($def->allow_null); - } - if (!isset($this->fields[$type])) { - $type = 0; - } // default - $type_obj = $this->fields[$type]; - if ($allow_null) { - $type_obj = new HTMLPurifier_Printer_ConfigForm_NullDecorator($type_obj); - } - $ret .= $type_obj->render($ns, $directive, $value, $this->name, array($this->genConfig, $this->config)); - $ret .= $this->end('td'); - $ret .= $this->end('tr'); - } - $ret .= $this->end('tbody'); - return $ret; - } - -} - -/** - * Printer decorator for directives that accept null - */ -class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer -{ - /** - * Printer being decorated - * @type HTMLPurifier_Printer - */ - protected $obj; - - /** - * @param HTMLPurifier_Printer $obj Printer to decorate - */ - public function __construct($obj) - { - parent::__construct(); - $this->obj = $obj; - } - - /** - * @param string $ns - * @param string $directive - * @param string $value - * @param string $name - * @param HTMLPurifier_Config|array $config - * @return string - */ - public function render($ns, $directive, $value, $name, $config) - { - if (is_array($config) && isset($config[0])) { - $gen_config = $config[0]; - $config = $config[1]; - } else { - $gen_config = $config; - } - $this->prepareGenerator($gen_config); - - $ret = ''; - $ret .= $this->start('label', array('for' => "$name:Null_$ns.$directive")); - $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose')); - $ret .= $this->text(' Null/Disabled'); - $ret .= $this->end('label'); - $attr = array( - 'type' => 'checkbox', - 'value' => '1', - 'class' => 'null-toggle', - 'name' => "$name" . "[Null_$ns.$directive]", - 'id' => "$name:Null_$ns.$directive", - 'onclick' => "toggleWriteability('$name:$ns.$directive',checked)" // INLINE JAVASCRIPT!!!! - ); - if ($this->obj instanceof HTMLPurifier_Printer_ConfigForm_bool) { - // modify inline javascript slightly - $attr['onclick'] = - "toggleWriteability('$name:Yes_$ns.$directive',checked);" . - "toggleWriteability('$name:No_$ns.$directive',checked)"; - } - if ($value === null) { - $attr['checked'] = 'checked'; - } - $ret .= $this->elementEmpty('input', $attr); - $ret .= $this->text(' or '); - $ret .= $this->elementEmpty('br'); - $ret .= $this->obj->render($ns, $directive, $value, $name, array($gen_config, $config)); - return $ret; - } -} - -/** - * Swiss-army knife configuration form field printer - */ -class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer -{ - /** - * @type int - */ - public $cols = 18; - - /** - * @type int - */ - public $rows = 5; - - /** - * @param string $ns - * @param string $directive - * @param string $value - * @param string $name - * @param HTMLPurifier_Config|array $config - * @return string - */ - public function render($ns, $directive, $value, $name, $config) - { - if (is_array($config) && isset($config[0])) { - $gen_config = $config[0]; - $config = $config[1]; - } else { - $gen_config = $config; - } - $this->prepareGenerator($gen_config); - // this should probably be split up a little - $ret = ''; - $def = $config->def->info["$ns.$directive"]; - if (is_int($def)) { - $type = abs($def); - } else { - $type = $def->type; - } - if (is_array($value)) { - switch ($type) { - case HTMLPurifier_VarParser::LOOKUP: - $array = $value; - $value = array(); - foreach ($array as $val => $b) { - $value[] = $val; - } - //TODO does this need a break? - case HTMLPurifier_VarParser::ALIST: - $value = implode(PHP_EOL, $value); - break; - case HTMLPurifier_VarParser::HASH: - $nvalue = ''; - foreach ($value as $i => $v) { - if (is_array($v)) { - // HACK - $v = implode(";", $v); - } - $nvalue .= "$i:$v" . PHP_EOL; - } - $value = $nvalue; - break; - default: - $value = ''; - } - } - if ($type === HTMLPurifier_VarParser::C_MIXED) { - return 'Not supported'; - $value = serialize($value); - } - $attr = array( - 'name' => "$name" . "[$ns.$directive]", - 'id' => "$name:$ns.$directive" - ); - if ($value === null) { - $attr['disabled'] = 'disabled'; - } - if (isset($def->allowed)) { - $ret .= $this->start('select', $attr); - foreach ($def->allowed as $val => $b) { - $attr = array(); - if ($value == $val) { - $attr['selected'] = 'selected'; - } - $ret .= $this->element('option', $val, $attr); - } - $ret .= $this->end('select'); - } elseif ($type === HTMLPurifier_VarParser::TEXT || - $type === HTMLPurifier_VarParser::ITEXT || - $type === HTMLPurifier_VarParser::ALIST || - $type === HTMLPurifier_VarParser::HASH || - $type === HTMLPurifier_VarParser::LOOKUP) { - $attr['cols'] = $this->cols; - $attr['rows'] = $this->rows; - $ret .= $this->start('textarea', $attr); - $ret .= $this->text($value); - $ret .= $this->end('textarea'); - } else { - $attr['value'] = $value; - $attr['type'] = 'text'; - $ret .= $this->elementEmpty('input', $attr); - } - return $ret; - } -} - -/** - * Bool form field printer - */ -class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer -{ - /** - * @param string $ns - * @param string $directive - * @param string $value - * @param string $name - * @param HTMLPurifier_Config|array $config - * @return string - */ - public function render($ns, $directive, $value, $name, $config) - { - if (is_array($config) && isset($config[0])) { - $gen_config = $config[0]; - $config = $config[1]; - } else { - $gen_config = $config; - } - $this->prepareGenerator($gen_config); - $ret = ''; - $ret .= $this->start('div', array('id' => "$name:$ns.$directive")); - - $ret .= $this->start('label', array('for' => "$name:Yes_$ns.$directive")); - $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose')); - $ret .= $this->text(' Yes'); - $ret .= $this->end('label'); - - $attr = array( - 'type' => 'radio', - 'name' => "$name" . "[$ns.$directive]", - 'id' => "$name:Yes_$ns.$directive", - 'value' => '1' - ); - if ($value === true) { - $attr['checked'] = 'checked'; - } - if ($value === null) { - $attr['disabled'] = 'disabled'; - } - $ret .= $this->elementEmpty('input', $attr); - - $ret .= $this->start('label', array('for' => "$name:No_$ns.$directive")); - $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose')); - $ret .= $this->text(' No'); - $ret .= $this->end('label'); - - $attr = array( - 'type' => 'radio', - 'name' => "$name" . "[$ns.$directive]", - 'id' => "$name:No_$ns.$directive", - 'value' => '0' - ); - if ($value === false) { - $attr['checked'] = 'checked'; - } - if ($value === null) { - $attr['disabled'] = 'disabled'; - } - $ret .= $this->elementEmpty('input', $attr); - - $ret .= $this->end('div'); - - return $ret; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php deleted file mode 100644 index ae8639176..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php +++ /dev/null @@ -1,324 +0,0 @@ -config =& $config; - - $this->def = $config->getHTMLDefinition(); - - $ret .= $this->start('div', array('class' => 'HTMLPurifier_Printer')); - - $ret .= $this->renderDoctype(); - $ret .= $this->renderEnvironment(); - $ret .= $this->renderContentSets(); - $ret .= $this->renderInfo(); - - $ret .= $this->end('div'); - - return $ret; - } - - /** - * Renders the Doctype table - * @return string - */ - protected function renderDoctype() - { - $doctype = $this->def->doctype; - $ret = ''; - $ret .= $this->start('table'); - $ret .= $this->element('caption', 'Doctype'); - $ret .= $this->row('Name', $doctype->name); - $ret .= $this->row('XML', $doctype->xml ? 'Yes' : 'No'); - $ret .= $this->row('Default Modules', implode(', ', $doctype->modules)); - $ret .= $this->row('Default Tidy Modules', implode(', ', $doctype->tidyModules)); - $ret .= $this->end('table'); - return $ret; - } - - - /** - * Renders environment table, which is miscellaneous info - * @return string - */ - protected function renderEnvironment() - { - $def = $this->def; - - $ret = ''; - - $ret .= $this->start('table'); - $ret .= $this->element('caption', 'Environment'); - - $ret .= $this->row('Parent of fragment', $def->info_parent); - $ret .= $this->renderChildren($def->info_parent_def->child); - $ret .= $this->row('Block wrap name', $def->info_block_wrapper); - - $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Global attributes'); - $ret .= $this->element('td', $this->listifyAttr($def->info_global_attr), null, 0); - $ret .= $this->end('tr'); - - $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Tag transforms'); - $list = array(); - foreach ($def->info_tag_transform as $old => $new) { - $new = $this->getClass($new, 'TagTransform_'); - $list[] = "<$old> with $new"; - } - $ret .= $this->element('td', $this->listify($list)); - $ret .= $this->end('tr'); - - $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Pre-AttrTransform'); - $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_pre)); - $ret .= $this->end('tr'); - - $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Post-AttrTransform'); - $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_post)); - $ret .= $this->end('tr'); - - $ret .= $this->end('table'); - return $ret; - } - - /** - * Renders the Content Sets table - * @return string - */ - protected function renderContentSets() - { - $ret = ''; - $ret .= $this->start('table'); - $ret .= $this->element('caption', 'Content Sets'); - foreach ($this->def->info_content_sets as $name => $lookup) { - $ret .= $this->heavyHeader($name); - $ret .= $this->start('tr'); - $ret .= $this->element('td', $this->listifyTagLookup($lookup)); - $ret .= $this->end('tr'); - } - $ret .= $this->end('table'); - return $ret; - } - - /** - * Renders the Elements ($info) table - * @return string - */ - protected function renderInfo() - { - $ret = ''; - $ret .= $this->start('table'); - $ret .= $this->element('caption', 'Elements ($info)'); - ksort($this->def->info); - $ret .= $this->heavyHeader('Allowed tags', 2); - $ret .= $this->start('tr'); - $ret .= $this->element('td', $this->listifyTagLookup($this->def->info), array('colspan' => 2)); - $ret .= $this->end('tr'); - foreach ($this->def->info as $name => $def) { - $ret .= $this->start('tr'); - $ret .= $this->element('th', "<$name>", array('class' => 'heavy', 'colspan' => 2)); - $ret .= $this->end('tr'); - $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Inline content'); - $ret .= $this->element('td', $def->descendants_are_inline ? 'Yes' : 'No'); - $ret .= $this->end('tr'); - if (!empty($def->excludes)) { - $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Excludes'); - $ret .= $this->element('td', $this->listifyTagLookup($def->excludes)); - $ret .= $this->end('tr'); - } - if (!empty($def->attr_transform_pre)) { - $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Pre-AttrTransform'); - $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_pre)); - $ret .= $this->end('tr'); - } - if (!empty($def->attr_transform_post)) { - $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Post-AttrTransform'); - $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_post)); - $ret .= $this->end('tr'); - } - if (!empty($def->auto_close)) { - $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Auto closed by'); - $ret .= $this->element('td', $this->listifyTagLookup($def->auto_close)); - $ret .= $this->end('tr'); - } - $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Allowed attributes'); - $ret .= $this->element('td', $this->listifyAttr($def->attr), array(), 0); - $ret .= $this->end('tr'); - - if (!empty($def->required_attr)) { - $ret .= $this->row('Required attributes', $this->listify($def->required_attr)); - } - - $ret .= $this->renderChildren($def->child); - } - $ret .= $this->end('table'); - return $ret; - } - - /** - * Renders a row describing the allowed children of an element - * @param HTMLPurifier_ChildDef $def HTMLPurifier_ChildDef of pertinent element - * @return string - */ - protected function renderChildren($def) - { - $context = new HTMLPurifier_Context(); - $ret = ''; - $ret .= $this->start('tr'); - $elements = array(); - $attr = array(); - if (isset($def->elements)) { - if ($def->type == 'strictblockquote') { - $def->validateChildren(array(), $this->config, $context); - } - $elements = $def->elements; - } - if ($def->type == 'chameleon') { - $attr['rowspan'] = 2; - } elseif ($def->type == 'empty') { - $elements = array(); - } elseif ($def->type == 'table') { - $elements = array_flip( - array( - 'col', - 'caption', - 'colgroup', - 'thead', - 'tfoot', - 'tbody', - 'tr' - ) - ); - } - $ret .= $this->element('th', 'Allowed children', $attr); - - if ($def->type == 'chameleon') { - - $ret .= $this->element( - 'td', - 'Block: ' . - $this->escape($this->listifyTagLookup($def->block->elements)), - null, - 0 - ); - $ret .= $this->end('tr'); - $ret .= $this->start('tr'); - $ret .= $this->element( - 'td', - 'Inline: ' . - $this->escape($this->listifyTagLookup($def->inline->elements)), - null, - 0 - ); - - } elseif ($def->type == 'custom') { - - $ret .= $this->element( - 'td', - '' . ucfirst($def->type) . ': ' . - $def->dtd_regex - ); - - } else { - $ret .= $this->element( - 'td', - '' . ucfirst($def->type) . ': ' . - $this->escape($this->listifyTagLookup($elements)), - null, - 0 - ); - } - $ret .= $this->end('tr'); - return $ret; - } - - /** - * Listifies a tag lookup table. - * @param array $array Tag lookup array in form of array('tagname' => true) - * @return string - */ - protected function listifyTagLookup($array) - { - ksort($array); - $list = array(); - foreach ($array as $name => $discard) { - if ($name !== '#PCDATA' && !isset($this->def->info[$name])) { - continue; - } - $list[] = $name; - } - return $this->listify($list); - } - - /** - * Listifies a list of objects by retrieving class names and internal state - * @param array $array List of objects - * @return string - * @todo Also add information about internal state - */ - protected function listifyObjectList($array) - { - ksort($array); - $list = array(); - foreach ($array as $obj) { - $list[] = $this->getClass($obj, 'AttrTransform_'); - } - return $this->listify($list); - } - - /** - * Listifies a hash of attributes to AttrDef classes - * @param array $array Array hash in form of array('attrname' => HTMLPurifier_AttrDef) - * @return string - */ - protected function listifyAttr($array) - { - ksort($array); - $list = array(); - foreach ($array as $name => $obj) { - if ($obj === false) { - continue; - } - $list[] = "$name = " . $this->getClass($obj, 'AttrDef_') . ''; - } - return $this->listify($list); - } - - /** - * Creates a heavy header row - * @param string $text - * @param int $num - * @return string - */ - protected function heavyHeader($text, $num = 1) - { - $ret = ''; - $ret .= $this->start('tr'); - $ret .= $this->element('th', $text, array('colspan' => $num, 'class' => 'heavy')); - $ret .= $this->end('tr'); - return $ret; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PropertyList.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PropertyList.php deleted file mode 100644 index 189348fd9..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PropertyList.php +++ /dev/null @@ -1,122 +0,0 @@ -parent = $parent; - } - - /** - * Recursively retrieves the value for a key - * @param string $name - * @throws HTMLPurifier_Exception - */ - public function get($name) - { - if ($this->has($name)) { - return $this->data[$name]; - } - // possible performance bottleneck, convert to iterative if necessary - if ($this->parent) { - return $this->parent->get($name); - } - throw new HTMLPurifier_Exception("Key '$name' not found"); - } - - /** - * Sets the value of a key, for this plist - * @param string $name - * @param mixed $value - */ - public function set($name, $value) - { - $this->data[$name] = $value; - } - - /** - * Returns true if a given key exists - * @param string $name - * @return bool - */ - public function has($name) - { - return array_key_exists($name, $this->data); - } - - /** - * Resets a value to the value of it's parent, usually the default. If - * no value is specified, the entire plist is reset. - * @param string $name - */ - public function reset($name = null) - { - if ($name == null) { - $this->data = array(); - } else { - unset($this->data[$name]); - } - } - - /** - * Squashes this property list and all of its property lists into a single - * array, and returns the array. This value is cached by default. - * @param bool $force If true, ignores the cache and regenerates the array. - * @return array - */ - public function squash($force = false) - { - if ($this->cache !== null && !$force) { - return $this->cache; - } - if ($this->parent) { - return $this->cache = array_merge($this->parent->squash($force), $this->data); - } else { - return $this->cache = $this->data; - } - } - - /** - * Returns the parent plist. - * @return HTMLPurifier_PropertyList - */ - public function getParent() - { - return $this->parent; - } - - /** - * Sets the parent plist. - * @param HTMLPurifier_PropertyList $plist Parent plist - */ - public function setParent($plist) - { - $this->parent = $plist; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PropertyListIterator.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PropertyListIterator.php deleted file mode 100644 index f68fc8c30..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PropertyListIterator.php +++ /dev/null @@ -1,43 +0,0 @@ -l = strlen($filter); - $this->filter = $filter; - } - - /** - * @return bool - */ - #[\ReturnTypeWillChange] - public function accept() - { - $key = $this->getInnerIterator()->key(); - if (strncmp($key, $this->filter, $this->l) !== 0) { - return false; - } - return true; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Queue.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Queue.php deleted file mode 100644 index f58db9042..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Queue.php +++ /dev/null @@ -1,56 +0,0 @@ -input = $input; - $this->output = array(); - } - - /** - * Shifts an element off the front of the queue. - */ - public function shift() { - if (empty($this->output)) { - $this->output = array_reverse($this->input); - $this->input = array(); - } - if (empty($this->output)) { - return NULL; - } - return array_pop($this->output); - } - - /** - * Pushes an element onto the front of the queue. - */ - public function push($x) { - array_push($this->input, $x); - } - - /** - * Checks if it's empty. - */ - public function isEmpty() { - return empty($this->input) && empty($this->output); - } -} diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy.php deleted file mode 100644 index e1ff3b72d..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy.php +++ /dev/null @@ -1,26 +0,0 @@ -strategies as $strategy) { - $tokens = $strategy->execute($tokens, $config, $context); - } - return $tokens; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Core.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Core.php deleted file mode 100644 index 4414c17d6..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Core.php +++ /dev/null @@ -1,17 +0,0 @@ -strategies[] = new HTMLPurifier_Strategy_RemoveForeignElements(); - $this->strategies[] = new HTMLPurifier_Strategy_MakeWellFormed(); - $this->strategies[] = new HTMLPurifier_Strategy_FixNesting(); - $this->strategies[] = new HTMLPurifier_Strategy_ValidateAttributes(); - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/FixNesting.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/FixNesting.php deleted file mode 100644 index 6fa673db9..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/FixNesting.php +++ /dev/null @@ -1,181 +0,0 @@ -getHTMLDefinition(); - - $excludes_enabled = !$config->get('Core.DisableExcludes'); - - // setup the context variable 'IsInline', for chameleon processing - // is 'false' when we are not inline, 'true' when it must always - // be inline, and an integer when it is inline for a certain - // branch of the document tree - $is_inline = $definition->info_parent_def->descendants_are_inline; - $context->register('IsInline', $is_inline); - - // setup error collector - $e =& $context->get('ErrorCollector', true); - - //####################################################################// - // Loop initialization - - // stack that contains all elements that are excluded - // it is organized by parent elements, similar to $stack, - // but it is only populated when an element with exclusions is - // processed, i.e. there won't be empty exclusions. - $exclude_stack = array($definition->info_parent_def->excludes); - - // variable that contains the start token while we are processing - // nodes. This enables error reporting to do its job - $node = $top_node; - // dummy token - list($token, $d) = $node->toTokenPair(); - $context->register('CurrentNode', $node); - $context->register('CurrentToken', $token); - - //####################################################################// - // Loop - - // We need to implement a post-order traversal iteratively, to - // avoid running into stack space limits. This is pretty tricky - // to reason about, so we just manually stack-ify the recursive - // variant: - // - // function f($node) { - // foreach ($node->children as $child) { - // f($child); - // } - // validate($node); - // } - // - // Thus, we will represent a stack frame as array($node, - // $is_inline, stack of children) - // e.g. array_reverse($node->children) - already processed - // children. - - $parent_def = $definition->info_parent_def; - $stack = array( - array($top_node, - $parent_def->descendants_are_inline, - $parent_def->excludes, // exclusions - 0) - ); - - while (!empty($stack)) { - list($node, $is_inline, $excludes, $ix) = array_pop($stack); - // recursive call - $go = false; - $def = empty($stack) ? $definition->info_parent_def : $definition->info[$node->name]; - while (isset($node->children[$ix])) { - $child = $node->children[$ix++]; - if ($child instanceof HTMLPurifier_Node_Element) { - $go = true; - $stack[] = array($node, $is_inline, $excludes, $ix); - $stack[] = array($child, - // ToDo: I don't think it matters if it's def or - // child_def, but double check this... - $is_inline || $def->descendants_are_inline, - empty($def->excludes) ? $excludes - : array_merge($excludes, $def->excludes), - 0); - break; - } - }; - if ($go) continue; - list($token, $d) = $node->toTokenPair(); - // base case - if ($excludes_enabled && isset($excludes[$node->name])) { - $node->dead = true; - if ($e) $e->send(E_ERROR, 'Strategy_FixNesting: Node excluded'); - } else { - // XXX I suppose it would be slightly more efficient to - // avoid the allocation here and have children - // strategies handle it - $children = array(); - foreach ($node->children as $child) { - if (!$child->dead) $children[] = $child; - } - $result = $def->child->validateChildren($children, $config, $context); - if ($result === true) { - // nop - $node->children = $children; - } elseif ($result === false) { - $node->dead = true; - if ($e) $e->send(E_ERROR, 'Strategy_FixNesting: Node removed'); - } else { - $node->children = $result; - if ($e) { - // XXX This will miss mutations of internal nodes. Perhaps defer to the child validators - if (empty($result) && !empty($children)) { - $e->send(E_ERROR, 'Strategy_FixNesting: Node contents removed'); - } else if ($result != $children) { - $e->send(E_WARNING, 'Strategy_FixNesting: Node reorganized'); - } - } - } - } - } - - //####################################################################// - // Post-processing - - // remove context variables - $context->destroy('IsInline'); - $context->destroy('CurrentNode'); - $context->destroy('CurrentToken'); - - //####################################################################// - // Return - - return HTMLPurifier_Arborize::flatten($node, $config, $context); - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/MakeWellFormed.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/MakeWellFormed.php deleted file mode 100644 index a6eb09e45..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/MakeWellFormed.php +++ /dev/null @@ -1,659 +0,0 @@ -getHTMLDefinition(); - - // local variables - $generator = new HTMLPurifier_Generator($config, $context); - $escape_invalid_tags = $config->get('Core.EscapeInvalidTags'); - // used for autoclose early abortion - $global_parent_allowed_elements = $definition->info_parent_def->child->getAllowedElements($config); - $e = $context->get('ErrorCollector', true); - $i = false; // injector index - list($zipper, $token) = HTMLPurifier_Zipper::fromArray($tokens); - if ($token === NULL) { - return array(); - } - $reprocess = false; // whether or not to reprocess the same token - $stack = array(); - - // member variables - $this->stack =& $stack; - $this->tokens =& $tokens; - $this->token =& $token; - $this->zipper =& $zipper; - $this->config = $config; - $this->context = $context; - - // context variables - $context->register('CurrentNesting', $stack); - $context->register('InputZipper', $zipper); - $context->register('CurrentToken', $token); - - // -- begin INJECTOR -- - - $this->injectors = array(); - - $injectors = $config->getBatch('AutoFormat'); - $def_injectors = $definition->info_injector; - $custom_injectors = $injectors['Custom']; - unset($injectors['Custom']); // special case - foreach ($injectors as $injector => $b) { - // XXX: Fix with a legitimate lookup table of enabled filters - if (strpos($injector, '.') !== false) { - continue; - } - $injector = "HTMLPurifier_Injector_$injector"; - if (!$b) { - continue; - } - $this->injectors[] = new $injector; - } - foreach ($def_injectors as $injector) { - // assumed to be objects - $this->injectors[] = $injector; - } - foreach ($custom_injectors as $injector) { - if (!$injector) { - continue; - } - if (is_string($injector)) { - $injector = "HTMLPurifier_Injector_$injector"; - $injector = new $injector; - } - $this->injectors[] = $injector; - } - - // give the injectors references to the definition and context - // variables for performance reasons - foreach ($this->injectors as $ix => $injector) { - $error = $injector->prepare($config, $context); - if (!$error) { - continue; - } - array_splice($this->injectors, $ix, 1); // rm the injector - trigger_error("Cannot enable {$injector->name} injector because $error is not allowed", E_USER_WARNING); - } - - // -- end INJECTOR -- - - // a note on reprocessing: - // In order to reduce code duplication, whenever some code needs - // to make HTML changes in order to make things "correct", the - // new HTML gets sent through the purifier, regardless of its - // status. This means that if we add a start token, because it - // was totally necessary, we don't have to update nesting; we just - // punt ($reprocess = true; continue;) and it does that for us. - - // isset is in loop because $tokens size changes during loop exec - for (;; - // only increment if we don't need to reprocess - $reprocess ? $reprocess = false : $token = $zipper->next($token)) { - - // check for a rewind - if (is_int($i)) { - // possibility: disable rewinding if the current token has a - // rewind set on it already. This would offer protection from - // infinite loop, but might hinder some advanced rewinding. - $rewind_offset = $this->injectors[$i]->getRewindOffset(); - if (is_int($rewind_offset)) { - for ($j = 0; $j < $rewind_offset; $j++) { - if (empty($zipper->front)) break; - $token = $zipper->prev($token); - // indicate that other injectors should not process this token, - // but we need to reprocess it. See Note [Injector skips] - unset($token->skip[$i]); - $token->rewind = $i; - if ($token instanceof HTMLPurifier_Token_Start) { - array_pop($this->stack); - } elseif ($token instanceof HTMLPurifier_Token_End) { - $this->stack[] = $token->start; - } - } - } - $i = false; - } - - // handle case of document end - if ($token === NULL) { - // kill processing if stack is empty - if (empty($this->stack)) { - break; - } - - // peek - $top_nesting = array_pop($this->stack); - $this->stack[] = $top_nesting; - - // send error [TagClosedSuppress] - if ($e && !isset($top_nesting->armor['MakeWellFormed_TagClosedError'])) { - $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by document end', $top_nesting); - } - - // append, don't splice, since this is the end - $token = new HTMLPurifier_Token_End($top_nesting->name); - - // punt! - $reprocess = true; - continue; - } - - //echo '
                '; printZipper($zipper, $token);//printTokens($this->stack); - //flush(); - - // quick-check: if it's not a tag, no need to process - if (empty($token->is_tag)) { - if ($token instanceof HTMLPurifier_Token_Text) { - foreach ($this->injectors as $i => $injector) { - if (isset($token->skip[$i])) { - // See Note [Injector skips] - continue; - } - if ($token->rewind !== null && $token->rewind !== $i) { - continue; - } - // XXX fuckup - $r = $token; - $injector->handleText($r); - $token = $this->processToken($r, $i); - $reprocess = true; - break; - } - } - // another possibility is a comment - continue; - } - - if (isset($definition->info[$token->name])) { - $type = $definition->info[$token->name]->child->type; - } else { - $type = false; // Type is unknown, treat accordingly - } - - // quick tag checks: anything that's *not* an end tag - $ok = false; - if ($type === 'empty' && $token instanceof HTMLPurifier_Token_Start) { - // claims to be a start tag but is empty - $token = new HTMLPurifier_Token_Empty( - $token->name, - $token->attr, - $token->line, - $token->col, - $token->armor - ); - $ok = true; - } elseif ($type && $type !== 'empty' && $token instanceof HTMLPurifier_Token_Empty) { - // claims to be empty but really is a start tag - // NB: this assignment is required - $old_token = $token; - $token = new HTMLPurifier_Token_End($token->name); - $token = $this->insertBefore( - new HTMLPurifier_Token_Start($old_token->name, $old_token->attr, $old_token->line, $old_token->col, $old_token->armor) - ); - // punt (since we had to modify the input stream in a non-trivial way) - $reprocess = true; - continue; - } elseif ($token instanceof HTMLPurifier_Token_Empty) { - // real empty token - $ok = true; - } elseif ($token instanceof HTMLPurifier_Token_Start) { - // start tag - - // ...unless they also have to close their parent - if (!empty($this->stack)) { - - // Performance note: you might think that it's rather - // inefficient, recalculating the autoclose information - // for every tag that a token closes (since when we - // do an autoclose, we push a new token into the - // stream and then /process/ that, before - // re-processing this token.) But this is - // necessary, because an injector can make an - // arbitrary transformations to the autoclosing - // tokens we introduce, so things may have changed - // in the meantime. Also, doing the inefficient thing is - // "easy" to reason about (for certain perverse definitions - // of "easy") - - $parent = array_pop($this->stack); - $this->stack[] = $parent; - - $parent_def = null; - $parent_elements = null; - $autoclose = false; - if (isset($definition->info[$parent->name])) { - $parent_def = $definition->info[$parent->name]; - $parent_elements = $parent_def->child->getAllowedElements($config); - $autoclose = !isset($parent_elements[$token->name]); - } - - if ($autoclose && $definition->info[$token->name]->wrap) { - // Check if an element can be wrapped by another - // element to make it valid in a context (for - // example,
                    needs a
                  • in between) - $wrapname = $definition->info[$token->name]->wrap; - $wrapdef = $definition->info[$wrapname]; - $elements = $wrapdef->child->getAllowedElements($config); - if (isset($elements[$token->name]) && isset($parent_elements[$wrapname])) { - $newtoken = new HTMLPurifier_Token_Start($wrapname); - $token = $this->insertBefore($newtoken); - $reprocess = true; - continue; - } - } - - $carryover = false; - if ($autoclose && $parent_def->formatting) { - $carryover = true; - } - - if ($autoclose) { - // check if this autoclose is doomed to fail - // (this rechecks $parent, which his harmless) - $autoclose_ok = isset($global_parent_allowed_elements[$token->name]); - if (!$autoclose_ok) { - foreach ($this->stack as $ancestor) { - $elements = $definition->info[$ancestor->name]->child->getAllowedElements($config); - if (isset($elements[$token->name])) { - $autoclose_ok = true; - break; - } - if ($definition->info[$token->name]->wrap) { - $wrapname = $definition->info[$token->name]->wrap; - $wrapdef = $definition->info[$wrapname]; - $wrap_elements = $wrapdef->child->getAllowedElements($config); - if (isset($wrap_elements[$token->name]) && isset($elements[$wrapname])) { - $autoclose_ok = true; - break; - } - } - } - } - if ($autoclose_ok) { - // errors need to be updated - $new_token = new HTMLPurifier_Token_End($parent->name); - $new_token->start = $parent; - // [TagClosedSuppress] - if ($e && !isset($parent->armor['MakeWellFormed_TagClosedError'])) { - if (!$carryover) { - $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag auto closed', $parent); - } else { - $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag carryover', $parent); - } - } - if ($carryover) { - $element = clone $parent; - // [TagClosedAuto] - $element->armor['MakeWellFormed_TagClosedError'] = true; - $element->carryover = true; - $token = $this->processToken(array($new_token, $token, $element)); - } else { - $token = $this->insertBefore($new_token); - } - } else { - $token = $this->remove(); - } - $reprocess = true; - continue; - } - - } - $ok = true; - } - - if ($ok) { - foreach ($this->injectors as $i => $injector) { - if (isset($token->skip[$i])) { - // See Note [Injector skips] - continue; - } - if ($token->rewind !== null && $token->rewind !== $i) { - continue; - } - $r = $token; - $injector->handleElement($r); - $token = $this->processToken($r, $i); - $reprocess = true; - break; - } - if (!$reprocess) { - // ah, nothing interesting happened; do normal processing - if ($token instanceof HTMLPurifier_Token_Start) { - $this->stack[] = $token; - } elseif ($token instanceof HTMLPurifier_Token_End) { - throw new HTMLPurifier_Exception( - 'Improper handling of end tag in start code; possible error in MakeWellFormed' - ); - } - } - continue; - } - - // sanity check: we should be dealing with a closing tag - if (!$token instanceof HTMLPurifier_Token_End) { - throw new HTMLPurifier_Exception('Unaccounted for tag token in input stream, bug in HTML Purifier'); - } - - // make sure that we have something open - if (empty($this->stack)) { - if ($escape_invalid_tags) { - if ($e) { - $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag to text'); - } - $token = new HTMLPurifier_Token_Text($generator->generateFromToken($token)); - } else { - if ($e) { - $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag removed'); - } - $token = $this->remove(); - } - $reprocess = true; - continue; - } - - // first, check for the simplest case: everything closes neatly. - // Eventually, everything passes through here; if there are problems - // we modify the input stream accordingly and then punt, so that - // the tokens get processed again. - $current_parent = array_pop($this->stack); - if ($current_parent->name == $token->name) { - $token->start = $current_parent; - foreach ($this->injectors as $i => $injector) { - if (isset($token->skip[$i])) { - // See Note [Injector skips] - continue; - } - if ($token->rewind !== null && $token->rewind !== $i) { - continue; - } - $r = $token; - $injector->handleEnd($r); - $token = $this->processToken($r, $i); - $this->stack[] = $current_parent; - $reprocess = true; - break; - } - continue; - } - - // okay, so we're trying to close the wrong tag - - // undo the pop previous pop - $this->stack[] = $current_parent; - - // scroll back the entire nest, trying to find our tag. - // (feature could be to specify how far you'd like to go) - $size = count($this->stack); - // -2 because -1 is the last element, but we already checked that - $skipped_tags = false; - for ($j = $size - 2; $j >= 0; $j--) { - if ($this->stack[$j]->name == $token->name) { - $skipped_tags = array_slice($this->stack, $j); - break; - } - } - - // we didn't find the tag, so remove - if ($skipped_tags === false) { - if ($escape_invalid_tags) { - if ($e) { - $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag to text'); - } - $token = new HTMLPurifier_Token_Text($generator->generateFromToken($token)); - } else { - if ($e) { - $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag removed'); - } - $token = $this->remove(); - } - $reprocess = true; - continue; - } - - // do errors, in REVERSE $j order: a,b,c with - $c = count($skipped_tags); - if ($e) { - for ($j = $c - 1; $j > 0; $j--) { - // notice we exclude $j == 0, i.e. the current ending tag, from - // the errors... [TagClosedSuppress] - if (!isset($skipped_tags[$j]->armor['MakeWellFormed_TagClosedError'])) { - $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by element end', $skipped_tags[$j]); - } - } - } - - // insert tags, in FORWARD $j order: c,b,a with - $replace = array($token); - for ($j = 1; $j < $c; $j++) { - // ...as well as from the insertions - $new_token = new HTMLPurifier_Token_End($skipped_tags[$j]->name); - $new_token->start = $skipped_tags[$j]; - array_unshift($replace, $new_token); - if (isset($definition->info[$new_token->name]) && $definition->info[$new_token->name]->formatting) { - // [TagClosedAuto] - $element = clone $skipped_tags[$j]; - $element->carryover = true; - $element->armor['MakeWellFormed_TagClosedError'] = true; - $replace[] = $element; - } - } - $token = $this->processToken($replace); - $reprocess = true; - continue; - } - - $context->destroy('CurrentToken'); - $context->destroy('CurrentNesting'); - $context->destroy('InputZipper'); - - unset($this->injectors, $this->stack, $this->tokens); - return $zipper->toArray($token); - } - - /** - * Processes arbitrary token values for complicated substitution patterns. - * In general: - * - * If $token is an array, it is a list of tokens to substitute for the - * current token. These tokens then get individually processed. If there - * is a leading integer in the list, that integer determines how many - * tokens from the stream should be removed. - * - * If $token is a regular token, it is swapped with the current token. - * - * If $token is false, the current token is deleted. - * - * If $token is an integer, that number of tokens (with the first token - * being the current one) will be deleted. - * - * @param HTMLPurifier_Token|array|int|bool $token Token substitution value - * @param HTMLPurifier_Injector|int $injector Injector that performed the substitution; default is if - * this is not an injector related operation. - * @throws HTMLPurifier_Exception - */ - protected function processToken($token, $injector = -1) - { - // Zend OpCache miscompiles $token = array($token), so - // avoid this pattern. See: https://github.com/ezyang/htmlpurifier/issues/108 - - // normalize forms of token - if (is_object($token)) { - $tmp = $token; - $token = array(1, $tmp); - } - if (is_int($token)) { - $tmp = $token; - $token = array($tmp); - } - if ($token === false) { - $token = array(1); - } - if (!is_array($token)) { - throw new HTMLPurifier_Exception('Invalid token type from injector'); - } - if (!is_int($token[0])) { - array_unshift($token, 1); - } - if ($token[0] === 0) { - throw new HTMLPurifier_Exception('Deleting zero tokens is not valid'); - } - - // $token is now an array with the following form: - // array(number nodes to delete, new node 1, new node 2, ...) - - $delete = array_shift($token); - list($old, $r) = $this->zipper->splice($this->token, $delete, $token); - - if ($injector > -1) { - // See Note [Injector skips] - // Determine appropriate skips. Here's what the code does: - // *If* we deleted one or more tokens, copy the skips - // of those tokens into the skips of the new tokens (in $token). - // Also, mark the newly inserted tokens as having come from - // $injector. - $oldskip = isset($old[0]) ? $old[0]->skip : array(); - foreach ($token as $object) { - $object->skip = $oldskip; - $object->skip[$injector] = true; - } - } - - return $r; - - } - - /** - * Inserts a token before the current token. Cursor now points to - * this token. You must reprocess after this. - * @param HTMLPurifier_Token $token - */ - private function insertBefore($token) - { - // NB not $this->zipper->insertBefore(), due to positioning - // differences - $splice = $this->zipper->splice($this->token, 0, array($token)); - - return $splice[1]; - } - - /** - * Removes current token. Cursor now points to new token occupying previously - * occupied space. You must reprocess after this. - */ - private function remove() - { - return $this->zipper->delete(); - } -} - -// Note [Injector skips] -// ~~~~~~~~~~~~~~~~~~~~~ -// When I originally designed this class, the idea behind the 'skip' -// property of HTMLPurifier_Token was to help avoid infinite loops -// in injector processing. For example, suppose you wrote an injector -// that bolded swear words. Naively, you might write it so that -// whenever you saw ****, you replaced it with ****. -// -// When this happens, we will reprocess all of the tokens with the -// other injectors. Now there is an opportunity for infinite loop: -// if we rerun the swear-word injector on these tokens, we might -// see **** and then reprocess again to get -// **** ad infinitum. -// -// Thus, the idea of a skip is that once we process a token with -// an injector, we mark all of those tokens as having "come from" -// the injector, and we never run the injector again on these -// tokens. -// -// There were two more complications, however: -// -// - With HTMLPurifier_Injector_RemoveEmpty, we noticed that if -// you had , after you removed the , you -// really would like this injector to go back and reprocess -// the tag, discovering that it is now empty and can be -// removed. So we reintroduced the possibility of infinite looping -// by adding a "rewind" function, which let you go back to an -// earlier point in the token stream and reprocess it with injectors. -// Needless to say, we need to UN-skip the token so it gets -// reprocessed. -// -// - Suppose that you successfuly process a token, replace it with -// one with your skip mark, but now another injector wants to -// process the skipped token with another token. Should you continue -// to skip that new token, or reprocess it? If you reprocess, -// you can end up with an infinite loop where one injector converts -// to , and then another injector converts it back. So -// we inherit the skips, but for some reason, I thought that we -// should inherit the skip from the first token of the token -// that we deleted. Why? Well, it seems to work OK. -// -// If I were to redesign this functionality, I would absolutely not -// go about doing it this way: the semantics are just not very well -// defined, and in any case you probably wanted to operate on trees, -// not token streams. - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/RemoveForeignElements.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/RemoveForeignElements.php deleted file mode 100644 index 1a8149ecc..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/RemoveForeignElements.php +++ /dev/null @@ -1,207 +0,0 @@ -getHTMLDefinition(); - $generator = new HTMLPurifier_Generator($config, $context); - $result = array(); - - $escape_invalid_tags = $config->get('Core.EscapeInvalidTags'); - $remove_invalid_img = $config->get('Core.RemoveInvalidImg'); - - // currently only used to determine if comments should be kept - $trusted = $config->get('HTML.Trusted'); - $comment_lookup = $config->get('HTML.AllowedComments'); - $comment_regexp = $config->get('HTML.AllowedCommentsRegexp'); - $check_comments = $comment_lookup !== array() || $comment_regexp !== null; - - $remove_script_contents = $config->get('Core.RemoveScriptContents'); - $hidden_elements = $config->get('Core.HiddenElements'); - - // remove script contents compatibility - if ($remove_script_contents === true) { - $hidden_elements['script'] = true; - } elseif ($remove_script_contents === false && isset($hidden_elements['script'])) { - unset($hidden_elements['script']); - } - - $attr_validator = new HTMLPurifier_AttrValidator(); - - // removes tokens until it reaches a closing tag with its value - $remove_until = false; - - // converts comments into text tokens when this is equal to a tag name - $textify_comments = false; - - $token = false; - $context->register('CurrentToken', $token); - - $e = false; - if ($config->get('Core.CollectErrors')) { - $e =& $context->get('ErrorCollector'); - } - - foreach ($tokens as $token) { - if ($remove_until) { - if (empty($token->is_tag) || $token->name !== $remove_until) { - continue; - } - } - if (!empty($token->is_tag)) { - // DEFINITION CALL - - // before any processing, try to transform the element - if (isset($definition->info_tag_transform[$token->name])) { - $original_name = $token->name; - // there is a transformation for this tag - // DEFINITION CALL - $token = $definition-> - info_tag_transform[$token->name]->transform($token, $config, $context); - if ($e) { - $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Tag transform', $original_name); - } - } - - if (isset($definition->info[$token->name])) { - // mostly everything's good, but - // we need to make sure required attributes are in order - if (($token instanceof HTMLPurifier_Token_Start || $token instanceof HTMLPurifier_Token_Empty) && - $definition->info[$token->name]->required_attr && - ($token->name != 'img' || $remove_invalid_img) // ensure config option still works - ) { - $attr_validator->validateToken($token, $config, $context); - $ok = true; - foreach ($definition->info[$token->name]->required_attr as $name) { - if (!isset($token->attr[$name])) { - $ok = false; - break; - } - } - if (!$ok) { - if ($e) { - $e->send( - E_ERROR, - 'Strategy_RemoveForeignElements: Missing required attribute', - $name - ); - } - continue; - } - $token->armor['ValidateAttributes'] = true; - } - - if (isset($hidden_elements[$token->name]) && $token instanceof HTMLPurifier_Token_Start) { - $textify_comments = $token->name; - } elseif ($token->name === $textify_comments && $token instanceof HTMLPurifier_Token_End) { - $textify_comments = false; - } - - } elseif ($escape_invalid_tags) { - // invalid tag, generate HTML representation and insert in - if ($e) { - $e->send(E_WARNING, 'Strategy_RemoveForeignElements: Foreign element to text'); - } - $token = new HTMLPurifier_Token_Text( - $generator->generateFromToken($token) - ); - } else { - // check if we need to destroy all of the tag's children - // CAN BE GENERICIZED - if (isset($hidden_elements[$token->name])) { - if ($token instanceof HTMLPurifier_Token_Start) { - $remove_until = $token->name; - } elseif ($token instanceof HTMLPurifier_Token_Empty) { - // do nothing: we're still looking - } else { - $remove_until = false; - } - if ($e) { - $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign meta element removed'); - } - } else { - if ($e) { - $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign element removed'); - } - } - continue; - } - } elseif ($token instanceof HTMLPurifier_Token_Comment) { - // textify comments in script tags when they are allowed - if ($textify_comments !== false) { - $data = $token->data; - $token = new HTMLPurifier_Token_Text($data); - } elseif ($trusted || $check_comments) { - // always cleanup comments - $trailing_hyphen = false; - if ($e) { - // perform check whether or not there's a trailing hyphen - if (substr($token->data, -1) == '-') { - $trailing_hyphen = true; - } - } - $token->data = rtrim($token->data, '-'); - $found_double_hyphen = false; - while (strpos($token->data, '--') !== false) { - $found_double_hyphen = true; - $token->data = str_replace('--', '-', $token->data); - } - if ($trusted || !empty($comment_lookup[trim($token->data)]) || - ($comment_regexp !== null && preg_match($comment_regexp, trim($token->data)))) { - // OK good - if ($e) { - if ($trailing_hyphen) { - $e->send( - E_NOTICE, - 'Strategy_RemoveForeignElements: Trailing hyphen in comment removed' - ); - } - if ($found_double_hyphen) { - $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Hyphens in comment collapsed'); - } - } - } else { - if ($e) { - $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed'); - } - continue; - } - } else { - // strip comments - if ($e) { - $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed'); - } - continue; - } - } elseif ($token instanceof HTMLPurifier_Token_Text) { - } else { - continue; - } - $result[] = $token; - } - if ($remove_until && $e) { - // we removed tokens until the end, throw error - $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Token removed to end', $remove_until); - } - $context->destroy('CurrentToken'); - return $result; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/ValidateAttributes.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/ValidateAttributes.php deleted file mode 100644 index fbb3d27c8..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/ValidateAttributes.php +++ /dev/null @@ -1,45 +0,0 @@ -register('CurrentToken', $token); - - foreach ($tokens as $key => $token) { - - // only process tokens that have attributes, - // namely start and empty tags - if (!$token instanceof HTMLPurifier_Token_Start && !$token instanceof HTMLPurifier_Token_Empty) { - continue; - } - - // skip tokens that are armored - if (!empty($token->armor['ValidateAttributes'])) { - continue; - } - - // note that we have no facilities here for removing tokens - $validator->validateToken($token, $config, $context); - } - $context->destroy('CurrentToken'); - return $tokens; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/StringHash.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/StringHash.php deleted file mode 100644 index c41ae3a76..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/StringHash.php +++ /dev/null @@ -1,48 +0,0 @@ -accessed[$index] = true; - return parent::offsetGet($index); - } - - /** - * Returns a lookup array of all array indexes that have been accessed. - * @return array in form array($index => true). - */ - public function getAccessed() - { - return $this->accessed; - } - - /** - * Resets the access array. - */ - public function resetAccessed() - { - $this->accessed = array(); - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/StringHashParser.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/StringHashParser.php deleted file mode 100644 index 7c73f8083..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/StringHashParser.php +++ /dev/null @@ -1,136 +0,0 @@ - 'DefaultKeyValue', - * 'KEY' => 'Value', - * 'KEY2' => 'Value2', - * 'MULTILINE-KEY' => "Multiline\nvalue.\n", - * ) - * - * We use this as an easy to use file-format for configuration schema - * files, but the class itself is usage agnostic. - * - * You can use ---- to forcibly terminate parsing of a single string-hash; - * this marker is used in multi string-hashes to delimit boundaries. - */ -class HTMLPurifier_StringHashParser -{ - - /** - * @type string - */ - public $default = 'ID'; - - /** - * Parses a file that contains a single string-hash. - * @param string $file - * @return array - */ - public function parseFile($file) - { - if (!file_exists($file)) { - return false; - } - $fh = fopen($file, 'r'); - if (!$fh) { - return false; - } - $ret = $this->parseHandle($fh); - fclose($fh); - return $ret; - } - - /** - * Parses a file that contains multiple string-hashes delimited by '----' - * @param string $file - * @return array - */ - public function parseMultiFile($file) - { - if (!file_exists($file)) { - return false; - } - $ret = array(); - $fh = fopen($file, 'r'); - if (!$fh) { - return false; - } - while (!feof($fh)) { - $ret[] = $this->parseHandle($fh); - } - fclose($fh); - return $ret; - } - - /** - * Internal parser that acepts a file handle. - * @note While it's possible to simulate in-memory parsing by using - * custom stream wrappers, if such a use-case arises we should - * factor out the file handle into its own class. - * @param resource $fh File handle with pointer at start of valid string-hash - * block. - * @return array - */ - protected function parseHandle($fh) - { - $state = false; - $single = false; - $ret = array(); - do { - $line = fgets($fh); - if ($line === false) { - break; - } - $line = rtrim($line, "\n\r"); - if (!$state && $line === '') { - continue; - } - if ($line === '----') { - break; - } - if (strncmp('--#', $line, 3) === 0) { - // Comment - continue; - } elseif (strncmp('--', $line, 2) === 0) { - // Multiline declaration - $state = trim($line, '- '); - if (!isset($ret[$state])) { - $ret[$state] = ''; - } - continue; - } elseif (!$state) { - $single = true; - if (strpos($line, ':') !== false) { - // Single-line declaration - list($state, $line) = explode(':', $line, 2); - $line = trim($line); - } else { - // Use default declaration - $state = $this->default; - } - } - if ($single) { - $ret[$state] = $line; - $single = false; - $state = false; - } else { - $ret[$state] .= "$line\n"; - } - } while (!feof($fh)); - return $ret; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform.php deleted file mode 100644 index 7b8d83343..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform.php +++ /dev/null @@ -1,37 +0,0 @@ - 'xx-small', - '1' => 'xx-small', - '2' => 'small', - '3' => 'medium', - '4' => 'large', - '5' => 'x-large', - '6' => 'xx-large', - '7' => '300%', - '-1' => 'smaller', - '-2' => '60%', - '+1' => 'larger', - '+2' => '150%', - '+3' => '200%', - '+4' => '300%' - ); - - /** - * @param HTMLPurifier_Token_Tag $tag - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return HTMLPurifier_Token_End|string - */ - public function transform($tag, $config, $context) - { - if ($tag instanceof HTMLPurifier_Token_End) { - $new_tag = clone $tag; - $new_tag->name = $this->transform_to; - return $new_tag; - } - - $attr = $tag->attr; - $prepend_style = ''; - - // handle color transform - if (isset($attr['color'])) { - $prepend_style .= 'color:' . $attr['color'] . ';'; - unset($attr['color']); - } - - // handle face transform - if (isset($attr['face'])) { - $prepend_style .= 'font-family:' . $attr['face'] . ';'; - unset($attr['face']); - } - - // handle size transform - if (isset($attr['size'])) { - // normalize large numbers - if ($attr['size'] !== '') { - if ($attr['size'][0] == '+' || $attr['size'][0] == '-') { - $size = (int)$attr['size']; - if ($size < -2) { - $attr['size'] = '-2'; - } - if ($size > 4) { - $attr['size'] = '+4'; - } - } else { - $size = (int)$attr['size']; - if ($size > 7) { - $attr['size'] = '7'; - } - } - } - if (isset($this->_size_lookup[$attr['size']])) { - $prepend_style .= 'font-size:' . - $this->_size_lookup[$attr['size']] . ';'; - } - unset($attr['size']); - } - - if ($prepend_style) { - $attr['style'] = isset($attr['style']) ? - $prepend_style . $attr['style'] : - $prepend_style; - } - - $new_tag = clone $tag; - $new_tag->name = $this->transform_to; - $new_tag->attr = $attr; - - return $new_tag; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Simple.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Simple.php deleted file mode 100644 index 71bf10b91..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Simple.php +++ /dev/null @@ -1,44 +0,0 @@ -transform_to = $transform_to; - $this->style = $style; - } - - /** - * @param HTMLPurifier_Token_Tag $tag - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return string - */ - public function transform($tag, $config, $context) - { - $new_tag = clone $tag; - $new_tag->name = $this->transform_to; - if (!is_null($this->style) && - ($new_tag instanceof HTMLPurifier_Token_Start || $new_tag instanceof HTMLPurifier_Token_Empty) - ) { - $this->prependCSS($new_tag->attr, $this->style); - } - return $new_tag; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token.php deleted file mode 100644 index 84d3619a3..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token.php +++ /dev/null @@ -1,100 +0,0 @@ -line = $l; - $this->col = $c; - } - - /** - * Convenience function for DirectLex settings line/col position. - * @param int $l - * @param int $c - */ - public function rawPosition($l, $c) - { - if ($c === -1) { - $l++; - } - $this->line = $l; - $this->col = $c; - } - - /** - * Converts a token into its corresponding node. - */ - abstract public function toNode(); -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Comment.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Comment.php deleted file mode 100644 index 23453c705..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Comment.php +++ /dev/null @@ -1,38 +0,0 @@ -data = $data; - $this->line = $line; - $this->col = $col; - } - - public function toNode() { - return new HTMLPurifier_Node_Comment($this->data, $this->line, $this->col); - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Empty.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Empty.php deleted file mode 100644 index 78a95f555..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Empty.php +++ /dev/null @@ -1,15 +0,0 @@ -empty = true; - return $n; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/End.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/End.php deleted file mode 100644 index 59b38fdc5..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/End.php +++ /dev/null @@ -1,24 +0,0 @@ -toNode not supported!"); - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Start.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Start.php deleted file mode 100644 index 019f317ad..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Start.php +++ /dev/null @@ -1,10 +0,0 @@ -!empty($obj->is_tag) - * without having to use a function call is_a(). - * @type bool - */ - public $is_tag = true; - - /** - * The lower-case name of the tag, like 'a', 'b' or 'blockquote'. - * - * @note Strictly speaking, XML tags are case sensitive, so we shouldn't - * be lower-casing them, but these tokens cater to HTML tags, which are - * insensitive. - * @type string - */ - public $name; - - /** - * Associative array of the tag's attributes. - * @type array - */ - public $attr = array(); - - /** - * Non-overloaded constructor, which lower-cases passed tag name. - * - * @param string $name String name. - * @param array $attr Associative array of attributes. - * @param int $line - * @param int $col - * @param array $armor - */ - public function __construct($name, $attr = array(), $line = null, $col = null, $armor = array()) - { - $this->name = ctype_lower($name) ? $name : strtolower($name); - foreach ($attr as $key => $value) { - // normalization only necessary when key is not lowercase - if (!ctype_lower($key)) { - $new_key = strtolower($key); - if (!isset($attr[$new_key])) { - $attr[$new_key] = $attr[$key]; - } - if ($new_key !== $key) { - unset($attr[$key]); - } - } - } - $this->attr = $attr; - $this->line = $line; - $this->col = $col; - $this->armor = $armor; - } - - public function toNode() { - return new HTMLPurifier_Node_Element($this->name, $this->attr, $this->line, $this->col, $this->armor); - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Text.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Text.php deleted file mode 100644 index f26a1c211..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Text.php +++ /dev/null @@ -1,53 +0,0 @@ -data = $data; - $this->is_whitespace = ctype_space($data); - $this->line = $line; - $this->col = $col; - } - - public function toNode() { - return new HTMLPurifier_Node_Text($this->data, $this->is_whitespace, $this->line, $this->col); - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TokenFactory.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TokenFactory.php deleted file mode 100644 index dea2446b9..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TokenFactory.php +++ /dev/null @@ -1,118 +0,0 @@ -p_start = new HTMLPurifier_Token_Start('', array()); - $this->p_end = new HTMLPurifier_Token_End(''); - $this->p_empty = new HTMLPurifier_Token_Empty('', array()); - $this->p_text = new HTMLPurifier_Token_Text(''); - $this->p_comment = new HTMLPurifier_Token_Comment(''); - } - - /** - * Creates a HTMLPurifier_Token_Start. - * @param string $name Tag name - * @param array $attr Associative array of attributes - * @return HTMLPurifier_Token_Start Generated HTMLPurifier_Token_Start - */ - public function createStart($name, $attr = array()) - { - $p = clone $this->p_start; - $p->__construct($name, $attr); - return $p; - } - - /** - * Creates a HTMLPurifier_Token_End. - * @param string $name Tag name - * @return HTMLPurifier_Token_End Generated HTMLPurifier_Token_End - */ - public function createEnd($name) - { - $p = clone $this->p_end; - $p->__construct($name); - return $p; - } - - /** - * Creates a HTMLPurifier_Token_Empty. - * @param string $name Tag name - * @param array $attr Associative array of attributes - * @return HTMLPurifier_Token_Empty Generated HTMLPurifier_Token_Empty - */ - public function createEmpty($name, $attr = array()) - { - $p = clone $this->p_empty; - $p->__construct($name, $attr); - return $p; - } - - /** - * Creates a HTMLPurifier_Token_Text. - * @param string $data Data of text token - * @return HTMLPurifier_Token_Text Generated HTMLPurifier_Token_Text - */ - public function createText($data) - { - $p = clone $this->p_text; - $p->__construct($data); - return $p; - } - - /** - * Creates a HTMLPurifier_Token_Comment. - * @param string $data Data of comment token - * @return HTMLPurifier_Token_Comment Generated HTMLPurifier_Token_Comment - */ - public function createComment($data) - { - $p = clone $this->p_comment; - $p->__construct($data); - return $p; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URI.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URI.php deleted file mode 100644 index 9c5be39d1..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URI.php +++ /dev/null @@ -1,316 +0,0 @@ -scheme = is_null($scheme) || ctype_lower($scheme) ? $scheme : strtolower($scheme); - $this->userinfo = $userinfo; - $this->host = $host; - $this->port = is_null($port) ? $port : (int)$port; - $this->path = $path; - $this->query = $query; - $this->fragment = $fragment; - } - - /** - * Retrieves a scheme object corresponding to the URI's scheme/default - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return HTMLPurifier_URIScheme Scheme object appropriate for validating this URI - */ - public function getSchemeObj($config, $context) - { - $registry = HTMLPurifier_URISchemeRegistry::instance(); - if ($this->scheme !== null) { - $scheme_obj = $registry->getScheme($this->scheme, $config, $context); - if (!$scheme_obj) { - return false; - } // invalid scheme, clean it out - } else { - // no scheme: retrieve the default one - $def = $config->getDefinition('URI'); - $scheme_obj = $def->getDefaultScheme($config, $context); - if (!$scheme_obj) { - if ($def->defaultScheme !== null) { - // something funky happened to the default scheme object - trigger_error( - 'Default scheme object "' . $def->defaultScheme . '" was not readable', - E_USER_WARNING - ); - } // suppress error if it's null - return false; - } - } - return $scheme_obj; - } - - /** - * Generic validation method applicable for all schemes. May modify - * this URI in order to get it into a compliant form. - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return bool True if validation/filtering succeeds, false if failure - */ - public function validate($config, $context) - { - // ABNF definitions from RFC 3986 - $chars_sub_delims = '!$&\'()*+,;='; - $chars_gen_delims = ':/?#[]@'; - $chars_pchar = $chars_sub_delims . ':@'; - - // validate host - if (!is_null($this->host)) { - $host_def = new HTMLPurifier_AttrDef_URI_Host(); - $this->host = $host_def->validate($this->host, $config, $context); - if ($this->host === false) { - $this->host = null; - } - } - - // validate scheme - // NOTE: It's not appropriate to check whether or not this - // scheme is in our registry, since a URIFilter may convert a - // URI that we don't allow into one we do. So instead, we just - // check if the scheme can be dropped because there is no host - // and it is our default scheme. - if (!is_null($this->scheme) && is_null($this->host) || $this->host === '') { - // support for relative paths is pretty abysmal when the - // scheme is present, so axe it when possible - $def = $config->getDefinition('URI'); - if ($def->defaultScheme === $this->scheme) { - $this->scheme = null; - } - } - - // validate username - if (!is_null($this->userinfo)) { - $encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . ':'); - $this->userinfo = $encoder->encode($this->userinfo); - } - - // validate port - if (!is_null($this->port)) { - if ($this->port < 1 || $this->port > 65535) { - $this->port = null; - } - } - - // validate path - $segments_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/'); - if (!is_null($this->host)) { // this catches $this->host === '' - // path-abempty (hier and relative) - // http://www.example.com/my/path - // //www.example.com/my/path (looks odd, but works, and - // recognized by most browsers) - // (this set is valid or invalid on a scheme by scheme - // basis, so we'll deal with it later) - // file:///my/path - // ///my/path - $this->path = $segments_encoder->encode($this->path); - } elseif ($this->path !== '') { - if ($this->path[0] === '/') { - // path-absolute (hier and relative) - // http:/my/path - // /my/path - if (strlen($this->path) >= 2 && $this->path[1] === '/') { - // This could happen if both the host gets stripped - // out - // http://my/path - // //my/path - $this->path = ''; - } else { - $this->path = $segments_encoder->encode($this->path); - } - } elseif (!is_null($this->scheme)) { - // path-rootless (hier) - // http:my/path - // Short circuit evaluation means we don't need to check nz - $this->path = $segments_encoder->encode($this->path); - } else { - // path-noscheme (relative) - // my/path - // (once again, not checking nz) - $segment_nc_encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . '@'); - $c = strpos($this->path, '/'); - if ($c !== false) { - $this->path = - $segment_nc_encoder->encode(substr($this->path, 0, $c)) . - $segments_encoder->encode(substr($this->path, $c)); - } else { - $this->path = $segment_nc_encoder->encode($this->path); - } - } - } else { - // path-empty (hier and relative) - $this->path = ''; // just to be safe - } - - // qf = query and fragment - $qf_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/?'); - - if (!is_null($this->query)) { - $this->query = $qf_encoder->encode($this->query); - } - - if (!is_null($this->fragment)) { - $this->fragment = $qf_encoder->encode($this->fragment); - } - return true; - } - - /** - * Convert URI back to string - * @return string URI appropriate for output - */ - public function toString() - { - // reconstruct authority - $authority = null; - // there is a rendering difference between a null authority - // (http:foo-bar) and an empty string authority - // (http:///foo-bar). - if (!is_null($this->host)) { - $authority = ''; - if (!is_null($this->userinfo)) { - $authority .= $this->userinfo . '@'; - } - $authority .= $this->host; - if (!is_null($this->port)) { - $authority .= ':' . $this->port; - } - } - - // Reconstruct the result - // One might wonder about parsing quirks from browsers after - // this reconstruction. Unfortunately, parsing behavior depends - // on what *scheme* was employed (file:///foo is handled *very* - // differently than http:///foo), so unfortunately we have to - // defer to the schemes to do the right thing. - $result = ''; - if (!is_null($this->scheme)) { - $result .= $this->scheme . ':'; - } - if (!is_null($authority)) { - $result .= '//' . $authority; - } - $result .= $this->path; - if (!is_null($this->query)) { - $result .= '?' . $this->query; - } - if (!is_null($this->fragment)) { - $result .= '#' . $this->fragment; - } - - return $result; - } - - /** - * Returns true if this URL might be considered a 'local' URL given - * the current context. This is true when the host is null, or - * when it matches the host supplied to the configuration. - * - * Note that this does not do any scheme checking, so it is mostly - * only appropriate for metadata that doesn't care about protocol - * security. isBenign is probably what you actually want. - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return bool - */ - public function isLocal($config, $context) - { - if ($this->host === null) { - return true; - } - $uri_def = $config->getDefinition('URI'); - if ($uri_def->host === $this->host) { - return true; - } - return false; - } - - /** - * Returns true if this URL should be considered a 'benign' URL, - * that is: - * - * - It is a local URL (isLocal), and - * - It has a equal or better level of security - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return bool - */ - public function isBenign($config, $context) - { - if (!$this->isLocal($config, $context)) { - return false; - } - - $scheme_obj = $this->getSchemeObj($config, $context); - if (!$scheme_obj) { - return false; - } // conservative approach - - $current_scheme_obj = $config->getDefinition('URI')->getDefaultScheme($config, $context); - if ($current_scheme_obj->secure) { - if (!$scheme_obj->secure) { - return false; - } - } - return true; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIDefinition.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIDefinition.php deleted file mode 100644 index e0bd8bcca..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIDefinition.php +++ /dev/null @@ -1,112 +0,0 @@ -registerFilter(new HTMLPurifier_URIFilter_DisableExternal()); - $this->registerFilter(new HTMLPurifier_URIFilter_DisableExternalResources()); - $this->registerFilter(new HTMLPurifier_URIFilter_DisableResources()); - $this->registerFilter(new HTMLPurifier_URIFilter_HostBlacklist()); - $this->registerFilter(new HTMLPurifier_URIFilter_SafeIframe()); - $this->registerFilter(new HTMLPurifier_URIFilter_MakeAbsolute()); - $this->registerFilter(new HTMLPurifier_URIFilter_Munge()); - } - - public function registerFilter($filter) - { - $this->registeredFilters[$filter->name] = $filter; - } - - public function addFilter($filter, $config) - { - $r = $filter->prepare($config); - if ($r === false) return; // null is ok, for backwards compat - if ($filter->post) { - $this->postFilters[$filter->name] = $filter; - } else { - $this->filters[$filter->name] = $filter; - } - } - - protected function doSetup($config) - { - $this->setupMemberVariables($config); - $this->setupFilters($config); - } - - protected function setupFilters($config) - { - foreach ($this->registeredFilters as $name => $filter) { - if ($filter->always_load) { - $this->addFilter($filter, $config); - } else { - $conf = $config->get('URI.' . $name); - if ($conf !== false && $conf !== null) { - $this->addFilter($filter, $config); - } - } - } - unset($this->registeredFilters); - } - - protected function setupMemberVariables($config) - { - $this->host = $config->get('URI.Host'); - $base_uri = $config->get('URI.Base'); - if (!is_null($base_uri)) { - $parser = new HTMLPurifier_URIParser(); - $this->base = $parser->parse($base_uri); - $this->defaultScheme = $this->base->scheme; - if (is_null($this->host)) $this->host = $this->base->host; - } - if (is_null($this->defaultScheme)) $this->defaultScheme = $config->get('URI.DefaultScheme'); - } - - public function getDefaultScheme($config, $context) - { - return HTMLPurifier_URISchemeRegistry::instance()->getScheme($this->defaultScheme, $config, $context); - } - - public function filter(&$uri, $config, $context) - { - foreach ($this->filters as $name => $f) { - $result = $f->filter($uri, $config, $context); - if (!$result) return false; - } - return true; - } - - public function postFilter(&$uri, $config, $context) - { - foreach ($this->postFilters as $name => $f) { - $result = $f->filter($uri, $config, $context); - if (!$result) return false; - } - return true; - } - -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter.php deleted file mode 100644 index 09724e9f4..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter.php +++ /dev/null @@ -1,74 +0,0 @@ -getDefinition('URI')->host; - if ($our_host !== null) { - $this->ourHostParts = array_reverse(explode('.', $our_host)); - } - } - - /** - * @param HTMLPurifier_URI $uri Reference - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return bool - */ - public function filter(&$uri, $config, $context) - { - if (is_null($uri->host)) { - return true; - } - if ($this->ourHostParts === false) { - return false; - } - $host_parts = array_reverse(explode('.', $uri->host)); - foreach ($this->ourHostParts as $i => $x) { - if (!isset($host_parts[$i])) { - return false; - } - if ($host_parts[$i] != $this->ourHostParts[$i]) { - return false; - } - } - return true; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php deleted file mode 100644 index c6562169e..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php +++ /dev/null @@ -1,25 +0,0 @@ -get('EmbeddedURI', true)) { - return true; - } - return parent::filter($uri, $config, $context); - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableResources.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableResources.php deleted file mode 100644 index d5c412c44..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableResources.php +++ /dev/null @@ -1,22 +0,0 @@ -get('EmbeddedURI', true); - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php deleted file mode 100644 index 32197c0e6..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php +++ /dev/null @@ -1,46 +0,0 @@ -blacklist = $config->get('URI.HostBlacklist'); - return true; - } - - /** - * @param HTMLPurifier_URI $uri - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return bool - */ - public function filter(&$uri, $config, $context) - { - foreach ($this->blacklist as $blacklisted_host_fragment) { - if ($uri->host !== null && strpos($uri->host, $blacklisted_host_fragment) !== false) { - return false; - } - } - return true; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php deleted file mode 100644 index c507bbff8..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php +++ /dev/null @@ -1,158 +0,0 @@ -getDefinition('URI'); - $this->base = $def->base; - if (is_null($this->base)) { - trigger_error( - 'URI.MakeAbsolute is being ignored due to lack of ' . - 'value for URI.Base configuration', - E_USER_WARNING - ); - return false; - } - $this->base->fragment = null; // fragment is invalid for base URI - $stack = explode('/', $this->base->path); - array_pop($stack); // discard last segment - $stack = $this->_collapseStack($stack); // do pre-parsing - $this->basePathStack = $stack; - return true; - } - - /** - * @param HTMLPurifier_URI $uri - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return bool - */ - public function filter(&$uri, $config, $context) - { - if (is_null($this->base)) { - return true; - } // abort early - if ($uri->path === '' && is_null($uri->scheme) && - is_null($uri->host) && is_null($uri->query) && is_null($uri->fragment)) { - // reference to current document - $uri = clone $this->base; - return true; - } - if (!is_null($uri->scheme)) { - // absolute URI already: don't change - if (!is_null($uri->host)) { - return true; - } - $scheme_obj = $uri->getSchemeObj($config, $context); - if (!$scheme_obj) { - // scheme not recognized - return false; - } - if (!$scheme_obj->hierarchical) { - // non-hierarchal URI with explicit scheme, don't change - return true; - } - // special case: had a scheme but always is hierarchical and had no authority - } - if (!is_null($uri->host)) { - // network path, don't bother - return true; - } - if ($uri->path === '') { - $uri->path = $this->base->path; - } elseif ($uri->path[0] !== '/') { - // relative path, needs more complicated processing - $stack = explode('/', $uri->path); - $new_stack = array_merge($this->basePathStack, $stack); - if ($new_stack[0] !== '' && !is_null($this->base->host)) { - array_unshift($new_stack, ''); - } - $new_stack = $this->_collapseStack($new_stack); - $uri->path = implode('/', $new_stack); - } else { - // absolute path, but still we should collapse - $uri->path = implode('/', $this->_collapseStack(explode('/', $uri->path))); - } - // re-combine - $uri->scheme = $this->base->scheme; - if (is_null($uri->userinfo)) { - $uri->userinfo = $this->base->userinfo; - } - if (is_null($uri->host)) { - $uri->host = $this->base->host; - } - if (is_null($uri->port)) { - $uri->port = $this->base->port; - } - return true; - } - - /** - * Resolve dots and double-dots in a path stack - * @param array $stack - * @return array - */ - private function _collapseStack($stack) - { - $result = array(); - $is_folder = false; - for ($i = 0; isset($stack[$i]); $i++) { - $is_folder = false; - // absorb an internally duplicated slash - if ($stack[$i] == '' && $i && isset($stack[$i + 1])) { - continue; - } - if ($stack[$i] == '..') { - if (!empty($result)) { - $segment = array_pop($result); - if ($segment === '' && empty($result)) { - // error case: attempted to back out too far: - // restore the leading slash - $result[] = ''; - } elseif ($segment === '..') { - $result[] = '..'; // cannot remove .. with .. - } - } else { - // relative path, preserve the double-dots - $result[] = '..'; - } - $is_folder = true; - continue; - } - if ($stack[$i] == '.') { - // silently absorb - $is_folder = true; - continue; - } - $result[] = $stack[$i]; - } - if ($is_folder) { - $result[] = ''; - } - return $result; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php deleted file mode 100644 index e1393deb7..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php +++ /dev/null @@ -1,115 +0,0 @@ -target = $config->get('URI.' . $this->name); - $this->parser = new HTMLPurifier_URIParser(); - $this->doEmbed = $config->get('URI.MungeResources'); - $this->secretKey = $config->get('URI.MungeSecretKey'); - if ($this->secretKey && !function_exists('hash_hmac')) { - throw new Exception("Cannot use %URI.MungeSecretKey without hash_hmac support."); - } - return true; - } - - /** - * @param HTMLPurifier_URI $uri - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return bool - */ - public function filter(&$uri, $config, $context) - { - if ($context->get('EmbeddedURI', true) && !$this->doEmbed) { - return true; - } - - $scheme_obj = $uri->getSchemeObj($config, $context); - if (!$scheme_obj) { - return true; - } // ignore unknown schemes, maybe another postfilter did it - if (!$scheme_obj->browsable) { - return true; - } // ignore non-browseable schemes, since we can't munge those in a reasonable way - if ($uri->isBenign($config, $context)) { - return true; - } // don't redirect if a benign URL - - $this->makeReplace($uri, $config, $context); - $this->replace = array_map('rawurlencode', $this->replace); - - $new_uri = strtr($this->target, $this->replace); - $new_uri = $this->parser->parse($new_uri); - // don't redirect if the target host is the same as the - // starting host - if ($uri->host === $new_uri->host) { - return true; - } - $uri = $new_uri; // overwrite - return true; - } - - /** - * @param HTMLPurifier_URI $uri - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - */ - protected function makeReplace($uri, $config, $context) - { - $string = $uri->toString(); - // always available - $this->replace['%s'] = $string; - $this->replace['%r'] = $context->get('EmbeddedURI', true) ?: ''; - $token = $context->get('CurrentToken', true) ?: ''; - $this->replace['%n'] = $token ? $token->name : ''; - $this->replace['%m'] = $context->get('CurrentAttr', true) ?: ''; - $this->replace['%p'] = $context->get('CurrentCSSProperty', true) ?: ''; - // not always available - if ($this->secretKey) { - $this->replace['%t'] = hash_hmac("sha256", $string, $this->secretKey); - } - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/SafeIframe.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/SafeIframe.php deleted file mode 100644 index f609c47a3..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/SafeIframe.php +++ /dev/null @@ -1,68 +0,0 @@ -regexp = $config->get('URI.SafeIframeRegexp'); - return true; - } - - /** - * @param HTMLPurifier_URI $uri - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return bool - */ - public function filter(&$uri, $config, $context) - { - // check if filter not applicable - if (!$config->get('HTML.SafeIframe')) { - return true; - } - // check if the filter should actually trigger - if (!$context->get('EmbeddedURI', true)) { - return true; - } - $token = $context->get('CurrentToken', true); - if (!($token && $token->name == 'iframe')) { - return true; - } - // check if we actually have some whitelists enabled - if ($this->regexp === null) { - return false; - } - // actually check the whitelists - return preg_match($this->regexp, $uri->toString()); - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIParser.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIParser.php deleted file mode 100644 index 0e7381a07..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIParser.php +++ /dev/null @@ -1,71 +0,0 @@ -percentEncoder = new HTMLPurifier_PercentEncoder(); - } - - /** - * Parses a URI. - * @param $uri string URI to parse - * @return HTMLPurifier_URI representation of URI. This representation has - * not been validated yet and may not conform to RFC. - */ - public function parse($uri) - { - $uri = $this->percentEncoder->normalize($uri); - - // Regexp is as per Appendix B. - // Note that ["<>] are an addition to the RFC's recommended - // characters, because they represent external delimeters. - $r_URI = '!'. - '(([a-zA-Z0-9\.\+\-]+):)?'. // 2. Scheme - '(//([^/?#"<>]*))?'. // 4. Authority - '([^?#"<>]*)'. // 5. Path - '(\?([^#"<>]*))?'. // 7. Query - '(#([^"<>]*))?'. // 8. Fragment - '!'; - - $matches = array(); - $result = preg_match($r_URI, $uri, $matches); - - if (!$result) return false; // *really* invalid URI - - // seperate out parts - $scheme = !empty($matches[1]) ? $matches[2] : null; - $authority = !empty($matches[3]) ? $matches[4] : null; - $path = $matches[5]; // always present, can be empty - $query = !empty($matches[6]) ? $matches[7] : null; - $fragment = !empty($matches[8]) ? $matches[9] : null; - - // further parse authority - if ($authority !== null) { - $r_authority = "/^((.+?)@)?(\[[^\]]+\]|[^:]*)(:(\d*))?/"; - $matches = array(); - preg_match($r_authority, $authority, $matches); - $userinfo = !empty($matches[1]) ? $matches[2] : null; - $host = !empty($matches[3]) ? $matches[3] : ''; - $port = !empty($matches[4]) ? (int) $matches[5] : null; - } else { - $port = $host = $userinfo = null; - } - - return new HTMLPurifier_URI( - $scheme, $userinfo, $host, $port, $path, $query, $fragment); - } - -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme.php deleted file mode 100644 index fe9e82cf2..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme.php +++ /dev/null @@ -1,102 +0,0 @@ -, resolves edge cases - * with making relative URIs absolute - * @type bool - */ - public $hierarchical = false; - - /** - * Whether or not the URI may omit a hostname when the scheme is - * explicitly specified, ala file:///path/to/file. As of writing, - * 'file' is the only scheme that browsers support his properly. - * @type bool - */ - public $may_omit_host = false; - - /** - * Validates the components of a URI for a specific scheme. - * @param HTMLPurifier_URI $uri Reference to a HTMLPurifier_URI object - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return bool success or failure - */ - abstract public function doValidate(&$uri, $config, $context); - - /** - * Public interface for validating components of a URI. Performs a - * bunch of default actions. Don't overload this method. - * @param HTMLPurifier_URI $uri Reference to a HTMLPurifier_URI object - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return bool success or failure - */ - public function validate(&$uri, $config, $context) - { - if ($this->default_port == $uri->port) { - $uri->port = null; - } - // kludge: browsers do funny things when the scheme but not the - // authority is set - if (!$this->may_omit_host && - // if the scheme is present, a missing host is always in error - (!is_null($uri->scheme) && ($uri->host === '' || is_null($uri->host))) || - // if the scheme is not present, a *blank* host is in error, - // since this translates into '///path' which most browsers - // interpret as being 'http://path'. - (is_null($uri->scheme) && $uri->host === '') - ) { - do { - if (is_null($uri->scheme)) { - if (substr($uri->path, 0, 2) != '//') { - $uri->host = null; - break; - } - // URI is '////path', so we cannot nullify the - // host to preserve semantics. Try expanding the - // hostname instead (fall through) - } - // first see if we can manually insert a hostname - $host = $config->get('URI.Host'); - if (!is_null($host)) { - $uri->host = $host; - } else { - // we can't do anything sensible, reject the URL. - return false; - } - } while (false); - } - return $this->doValidate($uri, $config, $context); - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/data.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/data.php deleted file mode 100644 index 41c49d553..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/data.php +++ /dev/null @@ -1,136 +0,0 @@ - true, - 'image/gif' => true, - 'image/png' => true, - ); - // this is actually irrelevant since we only write out the path - // component - /** - * @type bool - */ - public $may_omit_host = true; - - /** - * @param HTMLPurifier_URI $uri - * @param HTMLPurifier_Config $config - * @param HTMLPurifier_Context $context - * @return bool - */ - public function doValidate(&$uri, $config, $context) - { - $result = explode(',', $uri->path, 2); - $is_base64 = false; - $charset = null; - $content_type = null; - if (count($result) == 2) { - list($metadata, $data) = $result; - // do some legwork on the metadata - $metas = explode(';', $metadata); - while (!empty($metas)) { - $cur = array_shift($metas); - if ($cur == 'base64') { - $is_base64 = true; - break; - } - if (substr($cur, 0, 8) == 'charset=') { - // doesn't match if there are arbitrary spaces, but - // whatever dude - if ($charset !== null) { - continue; - } // garbage - $charset = substr($cur, 8); // not used - } else { - if ($content_type !== null) { - continue; - } // garbage - $content_type = $cur; - } - } - } else { - $data = $result[0]; - } - if ($content_type !== null && empty($this->allowed_types[$content_type])) { - return false; - } - if ($charset !== null) { - // error; we don't allow plaintext stuff - $charset = null; - } - $data = rawurldecode($data); - if ($is_base64) { - $raw_data = base64_decode($data); - } else { - $raw_data = $data; - } - if ( strlen($raw_data) < 12 ) { - // error; exif_imagetype throws exception with small files, - // and this likely indicates a corrupt URI/failed parse anyway - return false; - } - // XXX probably want to refactor this into a general mechanism - // for filtering arbitrary content types - if (function_exists('sys_get_temp_dir')) { - $file = tempnam(sys_get_temp_dir(), ""); - } else { - $file = tempnam("/tmp", ""); - } - file_put_contents($file, $raw_data); - if (function_exists('exif_imagetype')) { - $image_code = exif_imagetype($file); - unlink($file); - } elseif (function_exists('getimagesize')) { - set_error_handler(array($this, 'muteErrorHandler')); - $info = getimagesize($file); - restore_error_handler(); - unlink($file); - if ($info == false) { - return false; - } - $image_code = $info[2]; - } else { - trigger_error("could not find exif_imagetype or getimagesize functions", E_USER_ERROR); - } - $real_content_type = image_type_to_mime_type($image_code); - if ($real_content_type != $content_type) { - // we're nice guys; if the content type is something else we - // support, change it over - if (empty($this->allowed_types[$real_content_type])) { - return false; - } - $content_type = $real_content_type; - } - // ok, it's kosher, rewrite what we need - $uri->userinfo = null; - $uri->host = null; - $uri->port = null; - $uri->fragment = null; - $uri->query = null; - $uri->path = "$content_type;base64," . base64_encode($raw_data); - return true; - } - - /** - * @param int $errno - * @param string $errstr - */ - public function muteErrorHandler($errno, $errstr) - { - } -} diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/file.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/file.php deleted file mode 100644 index 215be4ba8..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/file.php +++ /dev/null @@ -1,44 +0,0 @@ -userinfo = null; - // file:// makes no provisions for accessing the resource - $uri->port = null; - // While it seems to work on Firefox, the querystring has - // no possible effect and is thus stripped. - $uri->query = null; - return true; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/ftp.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/ftp.php deleted file mode 100644 index 1eb43ee5c..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/ftp.php +++ /dev/null @@ -1,58 +0,0 @@ -query = null; - - // typecode check - $semicolon_pos = strrpos($uri->path, ';'); // reverse - if ($semicolon_pos !== false) { - $type = substr($uri->path, $semicolon_pos + 1); // no semicolon - $uri->path = substr($uri->path, 0, $semicolon_pos); - $type_ret = ''; - if (strpos($type, '=') !== false) { - // figure out whether or not the declaration is correct - list($key, $typecode) = explode('=', $type, 2); - if ($key !== 'type') { - // invalid key, tack it back on encoded - $uri->path .= '%3B' . $type; - } elseif ($typecode === 'a' || $typecode === 'i' || $typecode === 'd') { - $type_ret = ";type=$typecode"; - } - } else { - $uri->path .= '%3B' . $type; - } - $uri->path = str_replace(';', '%3B', $uri->path); - $uri->path .= $type_ret; - } - return true; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/http.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/http.php deleted file mode 100644 index ce69ec438..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/http.php +++ /dev/null @@ -1,36 +0,0 @@ -userinfo = null; - return true; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/https.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/https.php deleted file mode 100644 index 0e96882db..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/https.php +++ /dev/null @@ -1,18 +0,0 @@ -userinfo = null; - $uri->host = null; - $uri->port = null; - // we need to validate path against RFC 2368's addr-spec - return true; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/news.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/news.php deleted file mode 100644 index 7490927d6..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/news.php +++ /dev/null @@ -1,35 +0,0 @@ -userinfo = null; - $uri->host = null; - $uri->port = null; - $uri->query = null; - // typecode check needed on path - return true; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php deleted file mode 100644 index f211d715e..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php +++ /dev/null @@ -1,32 +0,0 @@ -userinfo = null; - $uri->query = null; - return true; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/tel.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/tel.php deleted file mode 100644 index 8cd193352..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/tel.php +++ /dev/null @@ -1,46 +0,0 @@ -userinfo = null; - $uri->host = null; - $uri->port = null; - - // Delete all non-numeric characters, non-x characters - // from phone number, EXCEPT for a leading plus sign. - $uri->path = preg_replace('/(?!^\+)[^\dx]/', '', - // Normalize e(x)tension to lower-case - str_replace('X', 'x', $uri->path)); - - return true; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URISchemeRegistry.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URISchemeRegistry.php deleted file mode 100644 index 4ac8a0b76..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URISchemeRegistry.php +++ /dev/null @@ -1,81 +0,0 @@ -get('URI.AllowedSchemes'); - if (!$config->get('URI.OverrideAllowedSchemes') && - !isset($allowed_schemes[$scheme]) - ) { - return; - } - - if (isset($this->schemes[$scheme])) { - return $this->schemes[$scheme]; - } - if (!isset($allowed_schemes[$scheme])) { - return; - } - - $class = 'HTMLPurifier_URIScheme_' . $scheme; - if (!class_exists($class)) { - return; - } - $this->schemes[$scheme] = new $class(); - return $this->schemes[$scheme]; - } - - /** - * Registers a custom scheme to the cache, bypassing reflection. - * @param string $scheme Scheme name - * @param HTMLPurifier_URIScheme $scheme_obj - */ - public function register($scheme, $scheme_obj) - { - $this->schemes[$scheme] = $scheme_obj; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/UnitConverter.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/UnitConverter.php deleted file mode 100644 index 166f3bf30..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/UnitConverter.php +++ /dev/null @@ -1,307 +0,0 @@ - array( - 'px' => 3, // This is as per CSS 2.1 and Firefox. Your mileage may vary - 'pt' => 4, - 'pc' => 48, - 'in' => 288, - self::METRIC => array('pt', '0.352777778', 'mm'), - ), - self::METRIC => array( - 'mm' => 1, - 'cm' => 10, - self::ENGLISH => array('mm', '2.83464567', 'pt'), - ), - ); - - /** - * Minimum bcmath precision for output. - * @type int - */ - protected $outputPrecision; - - /** - * Bcmath precision for internal calculations. - * @type int - */ - protected $internalPrecision; - - /** - * Whether or not BCMath is available. - * @type bool - */ - private $bcmath; - - public function __construct($output_precision = 4, $internal_precision = 10, $force_no_bcmath = false) - { - $this->outputPrecision = $output_precision; - $this->internalPrecision = $internal_precision; - $this->bcmath = !$force_no_bcmath && function_exists('bcmul'); - } - - /** - * Converts a length object of one unit into another unit. - * @param HTMLPurifier_Length $length - * Instance of HTMLPurifier_Length to convert. You must validate() - * it before passing it here! - * @param string $to_unit - * Unit to convert to. - * @return HTMLPurifier_Length|bool - * @note - * About precision: This conversion function pays very special - * attention to the incoming precision of values and attempts - * to maintain a number of significant figure. Results are - * fairly accurate up to nine digits. Some caveats: - * - If a number is zero-padded as a result of this significant - * figure tracking, the zeroes will be eliminated. - * - If a number contains less than four sigfigs ($outputPrecision) - * and this causes some decimals to be excluded, those - * decimals will be added on. - */ - public function convert($length, $to_unit) - { - if (!$length->isValid()) { - return false; - } - - $n = $length->getN(); - $unit = $length->getUnit(); - - if ($n === '0' || $unit === false) { - return new HTMLPurifier_Length('0', false); - } - - $state = $dest_state = false; - foreach (self::$units as $k => $x) { - if (isset($x[$unit])) { - $state = $k; - } - if (isset($x[$to_unit])) { - $dest_state = $k; - } - } - if (!$state || !$dest_state) { - return false; - } - - // Some calculations about the initial precision of the number; - // this will be useful when we need to do final rounding. - $sigfigs = $this->getSigFigs($n); - if ($sigfigs < $this->outputPrecision) { - $sigfigs = $this->outputPrecision; - } - - // BCMath's internal precision deals only with decimals. Use - // our default if the initial number has no decimals, or increase - // it by how ever many decimals, thus, the number of guard digits - // will always be greater than or equal to internalPrecision. - $log = (int)floor(log(abs($n), 10)); - $cp = ($log < 0) ? $this->internalPrecision - $log : $this->internalPrecision; // internal precision - - for ($i = 0; $i < 2; $i++) { - - // Determine what unit IN THIS SYSTEM we need to convert to - if ($dest_state === $state) { - // Simple conversion - $dest_unit = $to_unit; - } else { - // Convert to the smallest unit, pending a system shift - $dest_unit = self::$units[$state][$dest_state][0]; - } - - // Do the conversion if necessary - if ($dest_unit !== $unit) { - $factor = $this->div(self::$units[$state][$unit], self::$units[$state][$dest_unit], $cp); - $n = $this->mul($n, $factor, $cp); - $unit = $dest_unit; - } - - // Output was zero, so bail out early. Shouldn't ever happen. - if ($n === '') { - $n = '0'; - $unit = $to_unit; - break; - } - - // It was a simple conversion, so bail out - if ($dest_state === $state) { - break; - } - - if ($i !== 0) { - // Conversion failed! Apparently, the system we forwarded - // to didn't have this unit. This should never happen! - return false; - } - - // Pre-condition: $i == 0 - - // Perform conversion to next system of units - $n = $this->mul($n, self::$units[$state][$dest_state][1], $cp); - $unit = self::$units[$state][$dest_state][2]; - $state = $dest_state; - - // One more loop around to convert the unit in the new system. - - } - - // Post-condition: $unit == $to_unit - if ($unit !== $to_unit) { - return false; - } - - // Useful for debugging: - //echo "
                    n";
                    -        //echo "$n\nsigfigs = $sigfigs\nnew_log = $new_log\nlog = $log\nrp = $rp\n
                    \n"; - - $n = $this->round($n, $sigfigs); - if (strpos($n, '.') !== false) { - $n = rtrim($n, '0'); - } - $n = rtrim($n, '.'); - - return new HTMLPurifier_Length($n, $unit); - } - - /** - * Returns the number of significant figures in a string number. - * @param string $n Decimal number - * @return int number of sigfigs - */ - public function getSigFigs($n) - { - $n = ltrim($n, '0+-'); - $dp = strpos($n, '.'); // decimal position - if ($dp === false) { - $sigfigs = strlen(rtrim($n, '0')); - } else { - $sigfigs = strlen(ltrim($n, '0.')); // eliminate extra decimal character - if ($dp !== 0) { - $sigfigs--; - } - } - return $sigfigs; - } - - /** - * Adds two numbers, using arbitrary precision when available. - * @param string $s1 - * @param string $s2 - * @param int $scale - * @return string - */ - private function add($s1, $s2, $scale) - { - if ($this->bcmath) { - return bcadd($s1, $s2, $scale); - } else { - return $this->scale((float)$s1 + (float)$s2, $scale); - } - } - - /** - * Multiples two numbers, using arbitrary precision when available. - * @param string $s1 - * @param string $s2 - * @param int $scale - * @return string - */ - private function mul($s1, $s2, $scale) - { - if ($this->bcmath) { - return bcmul($s1, $s2, $scale); - } else { - return $this->scale((float)$s1 * (float)$s2, $scale); - } - } - - /** - * Divides two numbers, using arbitrary precision when available. - * @param string $s1 - * @param string $s2 - * @param int $scale - * @return string - */ - private function div($s1, $s2, $scale) - { - if ($this->bcmath) { - return bcdiv($s1, $s2, $scale); - } else { - return $this->scale((float)$s1 / (float)$s2, $scale); - } - } - - /** - * Rounds a number according to the number of sigfigs it should have, - * using arbitrary precision when available. - * @param float $n - * @param int $sigfigs - * @return string - */ - private function round($n, $sigfigs) - { - $new_log = (int)floor(log(abs($n), 10)); // Number of digits left of decimal - 1 - $rp = $sigfigs - $new_log - 1; // Number of decimal places needed - $neg = $n < 0 ? '-' : ''; // Negative sign - if ($this->bcmath) { - if ($rp >= 0) { - $n = bcadd($n, $neg . '0.' . str_repeat('0', $rp) . '5', $rp + 1); - $n = bcdiv($n, '1', $rp); - } else { - // This algorithm partially depends on the standardized - // form of numbers that comes out of bcmath. - $n = bcadd($n, $neg . '5' . str_repeat('0', $new_log - $sigfigs), 0); - $n = substr($n, 0, $sigfigs + strlen($neg)) . str_repeat('0', $new_log - $sigfigs + 1); - } - return $n; - } else { - return $this->scale(round($n, $sigfigs - $new_log - 1), $rp + 1); - } - } - - /** - * Scales a float to $scale digits right of decimal point, like BCMath. - * @param float $r - * @param int $scale - * @return string - */ - private function scale($r, $scale) - { - if ($scale < 0) { - // The f sprintf type doesn't support negative numbers, so we - // need to cludge things manually. First get the string. - $r = sprintf('%.0f', (float)$r); - // Due to floating point precision loss, $r will more than likely - // look something like 4652999999999.9234. We grab one more digit - // than we need to precise from $r and then use that to round - // appropriately. - $precise = (string)round(substr($r, 0, strlen($r) + $scale), -1); - // Now we return it, truncating the zero that was rounded off. - return substr($precise, 0, -1) . str_repeat('0', -$scale + 1); - } - return sprintf('%.' . $scale . 'f', (float)$r); - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser.php deleted file mode 100644 index 0c97c8289..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser.php +++ /dev/null @@ -1,198 +0,0 @@ - self::C_STRING, - 'istring' => self::ISTRING, - 'text' => self::TEXT, - 'itext' => self::ITEXT, - 'int' => self::C_INT, - 'float' => self::C_FLOAT, - 'bool' => self::C_BOOL, - 'lookup' => self::LOOKUP, - 'list' => self::ALIST, - 'hash' => self::HASH, - 'mixed' => self::C_MIXED - ); - - /** - * Lookup table of types that are string, and can have aliases or - * allowed value lists. - */ - public static $stringTypes = array( - self::C_STRING => true, - self::ISTRING => true, - self::TEXT => true, - self::ITEXT => true, - ); - - /** - * Validate a variable according to type. - * It may return NULL as a valid type if $allow_null is true. - * - * @param mixed $var Variable to validate - * @param int $type Type of variable, see HTMLPurifier_VarParser->types - * @param bool $allow_null Whether or not to permit null as a value - * @return string Validated and type-coerced variable - * @throws HTMLPurifier_VarParserException - */ - final public function parse($var, $type, $allow_null = false) - { - if (is_string($type)) { - if (!isset(HTMLPurifier_VarParser::$types[$type])) { - throw new HTMLPurifier_VarParserException("Invalid type '$type'"); - } else { - $type = HTMLPurifier_VarParser::$types[$type]; - } - } - $var = $this->parseImplementation($var, $type, $allow_null); - if ($allow_null && $var === null) { - return null; - } - // These are basic checks, to make sure nothing horribly wrong - // happened in our implementations. - switch ($type) { - case (self::C_STRING): - case (self::ISTRING): - case (self::TEXT): - case (self::ITEXT): - if (!is_string($var)) { - break; - } - if ($type == self::ISTRING || $type == self::ITEXT) { - $var = strtolower($var); - } - return $var; - case (self::C_INT): - if (!is_int($var)) { - break; - } - return $var; - case (self::C_FLOAT): - if (!is_float($var)) { - break; - } - return $var; - case (self::C_BOOL): - if (!is_bool($var)) { - break; - } - return $var; - case (self::LOOKUP): - case (self::ALIST): - case (self::HASH): - if (!is_array($var)) { - break; - } - if ($type === self::LOOKUP) { - foreach ($var as $k) { - if ($k !== true) { - $this->error('Lookup table contains value other than true'); - } - } - } elseif ($type === self::ALIST) { - $keys = array_keys($var); - if (array_keys($keys) !== $keys) { - $this->error('Indices for list are not uniform'); - } - } - return $var; - case (self::C_MIXED): - return $var; - default: - $this->errorInconsistent(get_class($this), $type); - } - $this->errorGeneric($var, $type); - } - - /** - * Actually implements the parsing. Base implementation does not - * do anything to $var. Subclasses should overload this! - * @param mixed $var - * @param int $type - * @param bool $allow_null - * @return string - */ - protected function parseImplementation($var, $type, $allow_null) - { - return $var; - } - - /** - * Throws an exception. - * @throws HTMLPurifier_VarParserException - */ - protected function error($msg) - { - throw new HTMLPurifier_VarParserException($msg); - } - - /** - * Throws an inconsistency exception. - * @note This should not ever be called. It would be called if we - * extend the allowed values of HTMLPurifier_VarParser without - * updating subclasses. - * @param string $class - * @param int $type - * @throws HTMLPurifier_Exception - */ - protected function errorInconsistent($class, $type) - { - throw new HTMLPurifier_Exception( - "Inconsistency in $class: " . HTMLPurifier_VarParser::getTypeName($type) . - " not implemented" - ); - } - - /** - * Generic error for if a type didn't work. - * @param mixed $var - * @param int $type - */ - protected function errorGeneric($var, $type) - { - $vtype = gettype($var); - $this->error("Expected type " . HTMLPurifier_VarParser::getTypeName($type) . ", got $vtype"); - } - - /** - * @param int $type - * @return string - */ - public static function getTypeName($type) - { - static $lookup; - if (!$lookup) { - // Lazy load the alternative lookup table - $lookup = array_flip(HTMLPurifier_VarParser::$types); - } - if (!isset($lookup[$type])) { - return 'unknown'; - } - return $lookup[$type]; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php deleted file mode 100644 index 3bfbe8386..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php +++ /dev/null @@ -1,130 +0,0 @@ - $j) { - $var[$i] = trim($j); - } - if ($type === self::HASH) { - // key:value,key2:value2 - $nvar = array(); - foreach ($var as $keypair) { - $c = explode(':', $keypair, 2); - if (!isset($c[1])) { - continue; - } - $nvar[trim($c[0])] = trim($c[1]); - } - $var = $nvar; - } - } - if (!is_array($var)) { - break; - } - $keys = array_keys($var); - if ($keys === array_keys($keys)) { - if ($type == self::ALIST) { - return $var; - } elseif ($type == self::LOOKUP) { - $new = array(); - foreach ($var as $key) { - $new[$key] = true; - } - return $new; - } else { - break; - } - } - if ($type === self::ALIST) { - trigger_error("Array list did not have consecutive integer indexes", E_USER_WARNING); - return array_values($var); - } - if ($type === self::LOOKUP) { - foreach ($var as $key => $value) { - if ($value !== true) { - trigger_error( - "Lookup array has non-true value at key '$key'; " . - "maybe your input array was not indexed numerically", - E_USER_WARNING - ); - } - $var[$key] = true; - } - } - return $var; - default: - $this->errorInconsistent(__CLASS__, $type); - } - $this->errorGeneric($var, $type); - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Native.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Native.php deleted file mode 100644 index f11c318ef..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Native.php +++ /dev/null @@ -1,38 +0,0 @@ -evalExpression($var); - } - - /** - * @param string $expr - * @return mixed - * @throws HTMLPurifier_VarParserException - */ - protected function evalExpression($expr) - { - $var = null; - $result = eval("\$var = $expr;"); - if ($result === false) { - throw new HTMLPurifier_VarParserException("Fatal error in evaluated code"); - } - return $var; - } -} - -// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParserException.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParserException.php deleted file mode 100644 index 5df341495..000000000 --- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParserException.php +++ /dev/null @@ -1,11 +0,0 @@ -front = $front; - $this->back = $back; - } - - /** - * Creates a zipper from an array, with a hole in the - * 0-index position. - * @param Array to zipper-ify. - * @return Tuple of zipper and element of first position. - */ - static public function fromArray($array) { - $z = new self(array(), array_reverse($array)); - $t = $z->delete(); // delete the "dummy hole" - return array($z, $t); - } - - /** - * Convert zipper back into a normal array, optionally filling in - * the hole with a value. (Usually you should supply a $t, unless you - * are at the end of the array.) - */ - public function toArray($t = NULL) { - $a = $this->front; - if ($t !== NULL) $a[] = $t; - for ($i = count($this->back)-1; $i >= 0; $i--) { - $a[] = $this->back[$i]; - } - return $a; - } - - /** - * Move hole to the next element. - * @param $t Element to fill hole with - * @return Original contents of new hole. - */ - public function next($t) { - if ($t !== NULL) array_push($this->front, $t); - return empty($this->back) ? NULL : array_pop($this->back); - } - - /** - * Iterated hole advancement. - * @param $t Element to fill hole with - * @param $i How many forward to advance hole - * @return Original contents of new hole, i away - */ - public function advance($t, $n) { - for ($i = 0; $i < $n; $i++) { - $t = $this->next($t); - } - return $t; - } - - /** - * Move hole to the previous element - * @param $t Element to fill hole with - * @return Original contents of new hole. - */ - public function prev($t) { - if ($t !== NULL) array_push($this->back, $t); - return empty($this->front) ? NULL : array_pop($this->front); - } - - /** - * Delete contents of current hole, shifting hole to - * next element. - * @return Original contents of new hole. - */ - public function delete() { - return empty($this->back) ? NULL : array_pop($this->back); - } - - /** - * Returns true if we are at the end of the list. - * @return bool - */ - public function done() { - return empty($this->back); - } - - /** - * Insert element before hole. - * @param Element to insert - */ - public function insertBefore($t) { - if ($t !== NULL) array_push($this->front, $t); - } - - /** - * Insert element after hole. - * @param Element to insert - */ - public function insertAfter($t) { - if ($t !== NULL) array_push($this->back, $t); - } - - /** - * Splice in multiple elements at hole. Functional specification - * in terms of array_splice: - * - * $arr1 = $arr; - * $old1 = array_splice($arr1, $i, $delete, $replacement); - * - * list($z, $t) = HTMLPurifier_Zipper::fromArray($arr); - * $t = $z->advance($t, $i); - * list($old2, $t) = $z->splice($t, $delete, $replacement); - * $arr2 = $z->toArray($t); - * - * assert($old1 === $old2); - * assert($arr1 === $arr2); - * - * NB: the absolute index location after this operation is - * *unchanged!* - * - * @param Current contents of hole. - */ - public function splice($t, $delete, $replacement) { - // delete - $old = array(); - $r = $t; - for ($i = $delete; $i > 0; $i--) { - $old[] = $r; - $r = $this->delete(); - } - // insert - for ($i = count($replacement)-1; $i >= 0; $i--) { - $this->insertAfter($r); - $r = $replacement[$i]; - } - return array($old, $r); - } -} diff --git a/vendor/fig/http-message-util/.gitignore b/vendor/fig/http-message-util/.gitignore new file mode 100644 index 000000000..48b8bf907 --- /dev/null +++ b/vendor/fig/http-message-util/.gitignore @@ -0,0 +1 @@ +vendor/ diff --git a/vendor/fig/http-message-util/CHANGELOG.md b/vendor/fig/http-message-util/CHANGELOG.md new file mode 100644 index 000000000..1a02e547f --- /dev/null +++ b/vendor/fig/http-message-util/CHANGELOG.md @@ -0,0 +1,147 @@ +# Changelog + +All notable changes to this project will be documented in this file, in reverse chronological order by release. + +## 1.1.5 - 2020-11-24 + +### Added + +- [#19](https://github.com/php-fig/http-message-util/pull/19) adds support for PHP 8. + +### Changed + +- Nothing. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. + +## 1.1.4 - 2020-02-05 + +### Added + +- Nothing. + +### Changed + +- Nothing. + +### Deprecated + +- Nothing. + +### Removed + +- [#15](https://github.com/php-fig/http-message-util/pull/15) removes the dependency on psr/http-message, as it is not technically necessary for usage of this package. + +### Fixed + +- Nothing. + +## 1.1.3 - 2018-11-19 + +### Added + +- [#10](https://github.com/php-fig/http-message-util/pull/10) adds the constants `StatusCodeInterface::STATUS_EARLY_HINTS` (103) and + `StatusCodeInterface::STATUS_TOO_EARLY` (425). + +### Changed + +- Nothing. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. + +## 1.1.2 - 2017-02-09 + +### Added + +- [#4](https://github.com/php-fig/http-message-util/pull/4) adds the constant + `StatusCodeInterface::STATUS_MISDIRECTED_REQUEST` (421). + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. + +## 1.1.1 - 2017-02-06 + +### Added + +- [#3](https://github.com/php-fig/http-message-util/pull/3) adds the constant + `StatusCodeInterface::STATUS_IM_A_TEAPOT` (418). + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. + +## 1.1.0 - 2016-09-19 + +### Added + +- [#1](https://github.com/php-fig/http-message-util/pull/1) adds + `Fig\Http\Message\StatusCodeInterface`, with constants named after common + status reason phrases, with values indicating the status codes themselves. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. + +## 1.0.0 - 2017-08-05 + +### Added + +- Adds `Fig\Http\Message\RequestMethodInterface`, with constants covering the + most common HTTP request methods as specified by the IETF. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. diff --git a/vendor/fig/http-message-util/LICENSE b/vendor/fig/http-message-util/LICENSE new file mode 100644 index 000000000..e2fa347ae --- /dev/null +++ b/vendor/fig/http-message-util/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2016 PHP Framework Interoperability Group + +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. diff --git a/vendor/fig/http-message-util/README.md b/vendor/fig/http-message-util/README.md new file mode 100644 index 000000000..ea5b5aa75 --- /dev/null +++ b/vendor/fig/http-message-util/README.md @@ -0,0 +1,17 @@ +# PSR Http Message Util + +This repository holds utility classes and constants to facilitate common +operations of [PSR-7](https://www.php-fig.org/psr/psr-7/); the primary purpose is +to provide constants for referring to request methods, response status codes and +messages, and potentially common headers. + +Implementation of PSR-7 interfaces is **not** within the scope of this package. + +## Installation + +Install by adding the package as a [Composer](https://getcomposer.org) +requirement: + +```bash +$ composer require fig/http-message-util +``` diff --git a/vendor/fig/http-message-util/composer.json b/vendor/fig/http-message-util/composer.json new file mode 100644 index 000000000..8645893b0 --- /dev/null +++ b/vendor/fig/http-message-util/composer.json @@ -0,0 +1,28 @@ +{ + "name": "fig/http-message-util", + "description": "Utility classes and constants for use with PSR-7 (psr/http-message)", + "keywords": ["psr", "psr-7", "http", "http-message", "request", "response"], + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "require": { + "php": "^5.3 || ^7.0 || ^8.0" + }, + "suggest": { + "psr/http-message": "The package containing the PSR-7 interfaces" + }, + "autoload": { + "psr-4": { + "Fig\\Http\\Message\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + } +} diff --git a/vendor/fig/http-message-util/src/RequestMethodInterface.php b/vendor/fig/http-message-util/src/RequestMethodInterface.php new file mode 100644 index 000000000..97d9a93bc --- /dev/null +++ b/vendor/fig/http-message-util/src/RequestMethodInterface.php @@ -0,0 +1,34 @@ + + * class RequestFactory implements RequestMethodInterface + * { + * public static function factory( + * $uri = '/', + * $method = self::METHOD_GET, + * $data = [] + * ) { + * } + * } + * + */ +interface RequestMethodInterface +{ + const METHOD_HEAD = 'HEAD'; + const METHOD_GET = 'GET'; + const METHOD_POST = 'POST'; + const METHOD_PUT = 'PUT'; + const METHOD_PATCH = 'PATCH'; + const METHOD_DELETE = 'DELETE'; + const METHOD_PURGE = 'PURGE'; + const METHOD_OPTIONS = 'OPTIONS'; + const METHOD_TRACE = 'TRACE'; + const METHOD_CONNECT = 'CONNECT'; +} diff --git a/vendor/fig/http-message-util/src/StatusCodeInterface.php b/vendor/fig/http-message-util/src/StatusCodeInterface.php new file mode 100644 index 000000000..99b7e780d --- /dev/null +++ b/vendor/fig/http-message-util/src/StatusCodeInterface.php @@ -0,0 +1,107 @@ + + * class ResponseFactory implements StatusCodeInterface + * { + * public function createResponse($code = self::STATUS_OK) + * { + * } + * } + * + */ +interface StatusCodeInterface +{ + // Informational 1xx + const STATUS_CONTINUE = 100; + const STATUS_SWITCHING_PROTOCOLS = 101; + const STATUS_PROCESSING = 102; + const STATUS_EARLY_HINTS = 103; + // Successful 2xx + const STATUS_OK = 200; + const STATUS_CREATED = 201; + const STATUS_ACCEPTED = 202; + const STATUS_NON_AUTHORITATIVE_INFORMATION = 203; + const STATUS_NO_CONTENT = 204; + const STATUS_RESET_CONTENT = 205; + const STATUS_PARTIAL_CONTENT = 206; + const STATUS_MULTI_STATUS = 207; + const STATUS_ALREADY_REPORTED = 208; + const STATUS_IM_USED = 226; + // Redirection 3xx + const STATUS_MULTIPLE_CHOICES = 300; + const STATUS_MOVED_PERMANENTLY = 301; + const STATUS_FOUND = 302; + const STATUS_SEE_OTHER = 303; + const STATUS_NOT_MODIFIED = 304; + const STATUS_USE_PROXY = 305; + const STATUS_RESERVED = 306; + const STATUS_TEMPORARY_REDIRECT = 307; + const STATUS_PERMANENT_REDIRECT = 308; + // Client Errors 4xx + const STATUS_BAD_REQUEST = 400; + const STATUS_UNAUTHORIZED = 401; + const STATUS_PAYMENT_REQUIRED = 402; + const STATUS_FORBIDDEN = 403; + const STATUS_NOT_FOUND = 404; + const STATUS_METHOD_NOT_ALLOWED = 405; + const STATUS_NOT_ACCEPTABLE = 406; + const STATUS_PROXY_AUTHENTICATION_REQUIRED = 407; + const STATUS_REQUEST_TIMEOUT = 408; + const STATUS_CONFLICT = 409; + const STATUS_GONE = 410; + const STATUS_LENGTH_REQUIRED = 411; + const STATUS_PRECONDITION_FAILED = 412; + const STATUS_PAYLOAD_TOO_LARGE = 413; + const STATUS_URI_TOO_LONG = 414; + const STATUS_UNSUPPORTED_MEDIA_TYPE = 415; + const STATUS_RANGE_NOT_SATISFIABLE = 416; + const STATUS_EXPECTATION_FAILED = 417; + const STATUS_IM_A_TEAPOT = 418; + const STATUS_MISDIRECTED_REQUEST = 421; + const STATUS_UNPROCESSABLE_ENTITY = 422; + const STATUS_LOCKED = 423; + const STATUS_FAILED_DEPENDENCY = 424; + const STATUS_TOO_EARLY = 425; + const STATUS_UPGRADE_REQUIRED = 426; + const STATUS_PRECONDITION_REQUIRED = 428; + const STATUS_TOO_MANY_REQUESTS = 429; + const STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; + const STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = 451; + // Server Errors 5xx + const STATUS_INTERNAL_SERVER_ERROR = 500; + const STATUS_NOT_IMPLEMENTED = 501; + const STATUS_BAD_GATEWAY = 502; + const STATUS_SERVICE_UNAVAILABLE = 503; + const STATUS_GATEWAY_TIMEOUT = 504; + const STATUS_VERSION_NOT_SUPPORTED = 505; + const STATUS_VARIANT_ALSO_NEGOTIATES = 506; + const STATUS_INSUFFICIENT_STORAGE = 507; + const STATUS_LOOP_DETECTED = 508; + const STATUS_NOT_EXTENDED = 510; + const STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511; +} diff --git a/vendor/firebase/php-jwt/CHANGELOG.md b/vendor/firebase/php-jwt/CHANGELOG.md index 5feeb5a67..7b5f6ce40 100644 --- a/vendor/firebase/php-jwt/CHANGELOG.md +++ b/vendor/firebase/php-jwt/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## [6.11.1](https://github.com/firebase/php-jwt/compare/v6.11.0...v6.11.1) (2025-04-09) + + +### Bug Fixes + +* update error text for consistency ([#528](https://github.com/firebase/php-jwt/issues/528)) ([c11113a](https://github.com/firebase/php-jwt/commit/c11113afa13265e016a669e75494b9203b8a7775)) + +## [6.11.0](https://github.com/firebase/php-jwt/compare/v6.10.2...v6.11.0) (2025-01-23) + + +### Features + +* support octet typed JWK ([#587](https://github.com/firebase/php-jwt/issues/587)) ([7cb8a26](https://github.com/firebase/php-jwt/commit/7cb8a265fa81edf2fa6ef8098f5bc5ae573c33ad)) + + +### Bug Fixes + +* refactor constructor Key to use PHP 8.0 syntax ([#577](https://github.com/firebase/php-jwt/issues/577)) ([29fa2ce](https://github.com/firebase/php-jwt/commit/29fa2ce9e0582cd397711eec1e80c05ce20fabca)) + ## [6.10.2](https://github.com/firebase/php-jwt/compare/v6.10.1...v6.10.2) (2024-11-24) diff --git a/vendor/firebase/php-jwt/README.md b/vendor/firebase/php-jwt/README.md index 4fd140745..e45ccb808 100644 --- a/vendor/firebase/php-jwt/README.md +++ b/vendor/firebase/php-jwt/README.md @@ -48,7 +48,8 @@ $decoded = JWT::decode($jwt, new Key($key, 'HS256')); print_r($decoded); // Pass a stdClass in as the third parameter to get the decoded header values -$decoded = JWT::decode($jwt, new Key($key, 'HS256'), $headers = new stdClass()); +$headers = new stdClass(); +$decoded = JWT::decode($jwt, new Key($key, 'HS256'), $headers); print_r($headers); /* @@ -290,7 +291,7 @@ $jwks = ['keys' => []]; // JWK::parseKeySet($jwks) returns an associative array of **kid** to Firebase\JWT\Key // objects. Pass this as the second parameter to JWT::decode. -JWT::decode($payload, JWK::parseKeySet($jwks)); +JWT::decode($jwt, JWK::parseKeySet($jwks)); ``` Using Cached Key Sets @@ -349,7 +350,7 @@ use InvalidArgumentException; use UnexpectedValueException; try { - $decoded = JWT::decode($payload, $keys); + $decoded = JWT::decode($jwt, $keys); } catch (InvalidArgumentException $e) { // provided key/key-array is empty or malformed. } catch (DomainException $e) { @@ -379,7 +380,7 @@ like this: use Firebase\JWT\JWT; use UnexpectedValueException; try { - $decoded = JWT::decode($payload, $keys); + $decoded = JWT::decode($jwt, $keys); } catch (LogicException $e) { // errors having to do with environmental setup or malformed JWT Keys } catch (UnexpectedValueException $e) { @@ -394,7 +395,7 @@ instead, you can do the following: ```php // return type is stdClass -$decoded = JWT::decode($payload, $keys); +$decoded = JWT::decode($jwt, $keys); // cast to array $decoded = json_decode(json_encode($decoded), true); diff --git a/vendor/firebase/php-jwt/src/JWK.php b/vendor/firebase/php-jwt/src/JWK.php index 6efc2fe38..405dcc49b 100644 --- a/vendor/firebase/php-jwt/src/JWK.php +++ b/vendor/firebase/php-jwt/src/JWK.php @@ -172,6 +172,12 @@ public static function parseKey(array $jwk, ?string $defaultAlg = null): ?Key // This library works internally with EdDSA keys (Ed25519) encoded in standard base64. $publicKey = JWT::convertBase64urlToBase64($jwk['x']); return new Key($publicKey, $jwk['alg']); + case 'oct': + if (!isset($jwk['k'])) { + throw new UnexpectedValueException('k not set'); + } + + return new Key(JWT::urlsafeB64Decode($jwk['k']), $jwk['alg']); default: break; } diff --git a/vendor/firebase/php-jwt/src/JWT.php b/vendor/firebase/php-jwt/src/JWT.php index dd9292a43..833a415e6 100644 --- a/vendor/firebase/php-jwt/src/JWT.php +++ b/vendor/firebase/php-jwt/src/JWT.php @@ -154,7 +154,7 @@ public static function decode( // token can actually be used. If it's not yet that time, abort. if (isset($payload->nbf) && floor($payload->nbf) > ($timestamp + static::$leeway)) { $ex = new BeforeValidException( - 'Cannot handle token with nbf prior to ' . \date(DateTime::ISO8601, (int) $payload->nbf) + 'Cannot handle token with nbf prior to ' . \date(DateTime::ISO8601, (int) floor($payload->nbf)) ); $ex->setPayload($payload); throw $ex; @@ -165,7 +165,7 @@ public static function decode( // correctly used the nbf claim). if (!isset($payload->nbf) && isset($payload->iat) && floor($payload->iat) > ($timestamp + static::$leeway)) { $ex = new BeforeValidException( - 'Cannot handle token with iat prior to ' . \date(DateTime::ISO8601, (int) $payload->iat) + 'Cannot handle token with iat prior to ' . \date(DateTime::ISO8601, (int) floor($payload->iat)) ); $ex->setPayload($payload); throw $ex; diff --git a/vendor/firebase/php-jwt/src/Key.php b/vendor/firebase/php-jwt/src/Key.php index 00cf7f2ed..b34eae258 100644 --- a/vendor/firebase/php-jwt/src/Key.php +++ b/vendor/firebase/php-jwt/src/Key.php @@ -9,18 +9,13 @@ class Key { - /** @var string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate */ - private $keyMaterial; - /** @var string */ - private $algorithm; - /** * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial * @param string $algorithm */ public function __construct( - $keyMaterial, - string $algorithm + private $keyMaterial, + private string $algorithm ) { if ( !\is_string($keyMaterial) @@ -38,10 +33,6 @@ public function __construct( if (empty($algorithm)) { throw new InvalidArgumentException('Algorithm must not be empty'); } - - // TODO: Remove in PHP 8.0 in favor of class constructor property promotion - $this->keyMaterial = $keyMaterial; - $this->algorithm = $algorithm; } /** diff --git a/vendor/guzzlehttp/.DS_Store b/vendor/guzzlehttp/.DS_Store new file mode 100644 index 000000000..55091de38 Binary files /dev/null and b/vendor/guzzlehttp/.DS_Store differ diff --git a/vendor/guzzlehttp/guzzle/CHANGELOG.md b/vendor/guzzlehttp/guzzle/CHANGELOG.md index e0b621659..7600ef497 100644 --- a/vendor/guzzlehttp/guzzle/CHANGELOG.md +++ b/vendor/guzzlehttp/guzzle/CHANGELOG.md @@ -3,6 +3,14 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version. +## 7.9.3 - 2025-03-27 + +### Changed + +- Remove explicit content-length header for GET requests +- Improve compatibility with bad servers for boolean cookie values + + ## 7.9.2 - 2024-07-24 ### Fixed diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php b/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php index c9806da88..47c4d10ae 100644 --- a/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php +++ b/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php @@ -62,6 +62,10 @@ public static function fromString(string $cookie): self if (is_numeric($value)) { $data[$search] = (int) $value; } + } elseif ($search === 'Secure' || $search === 'Discard' || $search === 'HttpOnly') { + if ($value) { + $data[$search] = true; + } } else { $data[$search] = $value; } diff --git a/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php b/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php index f045b526c..9df70cf23 100644 --- a/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php +++ b/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php @@ -17,10 +17,10 @@ class Proxy * Sends synchronous requests to a specific handler while sending all other * requests to another handler. * - * @param callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface $default Handler used for normal responses - * @param callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface $sync Handler used for synchronous responses. + * @param callable(RequestInterface, array): PromiseInterface $default Handler used for normal responses + * @param callable(RequestInterface, array): PromiseInterface $sync Handler used for synchronous responses. * - * @return callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface Returns the composed handler. + * @return callable(RequestInterface, array): PromiseInterface Returns the composed handler. */ public static function wrapSync(callable $default, callable $sync): callable { @@ -37,10 +37,10 @@ public static function wrapSync(callable $default, callable $sync): callable * performance benefits of curl while still supporting true streaming * through the StreamHandler. * - * @param callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface $default Handler used for non-streaming responses - * @param callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface $streaming Handler used for streaming responses + * @param callable(RequestInterface, array): PromiseInterface $default Handler used for non-streaming responses + * @param callable(RequestInterface, array): PromiseInterface $streaming Handler used for streaming responses * - * @return callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface Returns the composed handler. + * @return callable(RequestInterface, array): PromiseInterface Returns the composed handler. */ public static function wrapStreaming(callable $default, callable $streaming): callable { diff --git a/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php b/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php index 1d89a8fbc..083049ea8 100644 --- a/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php +++ b/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php @@ -53,8 +53,14 @@ public function __invoke(RequestInterface $request, array $options): PromiseInte $request = $request->withoutHeader('Expect'); // Append a content-length header if body size is zero to match - // cURL's behavior. - if (0 === $request->getBody()->getSize()) { + // the behavior of `CurlHandler` + if ( + ( + 0 === \strcasecmp('PUT', $request->getMethod()) + || 0 === \strcasecmp('POST', $request->getMethod()) + ) + && 0 === $request->getBody()->getSize() + ) { $request = $request->withHeader('Content-Length', '0'); } diff --git a/vendor/guzzlehttp/guzzle/src/Pool.php b/vendor/guzzlehttp/guzzle/src/Pool.php index 6277c61fb..ddc304bb1 100644 --- a/vendor/guzzlehttp/guzzle/src/Pool.php +++ b/vendor/guzzlehttp/guzzle/src/Pool.php @@ -86,7 +86,7 @@ public function promise(): PromiseInterface * @param ClientInterface $client Client used to send the requests * @param array|\Iterator $requests Requests to send concurrently. * @param array $options Passes through the options available in - * {@see \GuzzleHttp\Pool::__construct} + * {@see Pool::__construct} * * @return array Returns an array containing the response or an exception * in the same order that the requests were sent. diff --git a/vendor/guzzlehttp/guzzle/src/Utils.php b/vendor/guzzlehttp/guzzle/src/Utils.php index df529270e..c6a5893dd 100644 --- a/vendor/guzzlehttp/guzzle/src/Utils.php +++ b/vendor/guzzlehttp/guzzle/src/Utils.php @@ -79,7 +79,7 @@ public static function debugResource($value = null) * * The returned handler is not wrapped by any default middlewares. * - * @return callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface Returns the best handler for the given system. + * @return callable(\Psr\Http\Message\RequestInterface, array): Promise\PromiseInterface Returns the best handler for the given system. * * @throws \RuntimeException if no viable Handler is available. */ diff --git a/vendor/guzzlehttp/guzzle/src/functions.php b/vendor/guzzlehttp/guzzle/src/functions.php index 5edc66ab1..9ab4b9649 100644 --- a/vendor/guzzlehttp/guzzle/src/functions.php +++ b/vendor/guzzlehttp/guzzle/src/functions.php @@ -50,7 +50,7 @@ function debug_resource($value = null) * * The returned handler is not wrapped by any default middlewares. * - * @return callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface Returns the best handler for the given system. + * @return callable(\Psr\Http\Message\RequestInterface, array): Promise\PromiseInterface Returns the best handler for the given system. * * @throws \RuntimeException if no viable Handler is available. * diff --git a/vendor/guzzlehttp/promises/CHANGELOG.md b/vendor/guzzlehttp/promises/CHANGELOG.md index c7c49a144..1d8045032 100644 --- a/vendor/guzzlehttp/promises/CHANGELOG.md +++ b/vendor/guzzlehttp/promises/CHANGELOG.md @@ -1,6 +1,20 @@ # CHANGELOG +## 2.2.0 - 2025-03-27 + +### Fixed + +- Revert "Allow an empty EachPromise to be resolved by running the queue" + + +## 2.1.0 - 2025-03-27 + +### Added + +- Allow an empty EachPromise to be resolved by running the queue + + ## 2.0.4 - 2024-10-17 ### Fixed diff --git a/vendor/guzzlehttp/psr7/CHANGELOG.md b/vendor/guzzlehttp/psr7/CHANGELOG.md index 75aabfb93..a85929521 100644 --- a/vendor/guzzlehttp/psr7/CHANGELOG.md +++ b/vendor/guzzlehttp/psr7/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 2.7.1 - 2025-03-27 + +### Fixed + +- Fixed uppercase IPv6 addresses in URI + +### Changed + +- Improve uploaded file error message + ## 2.7.0 - 2024-07-18 ### Added diff --git a/vendor/guzzlehttp/psr7/src/UploadedFile.php b/vendor/guzzlehttp/psr7/src/UploadedFile.php index 9c9ea49fb..d9b779f13 100644 --- a/vendor/guzzlehttp/psr7/src/UploadedFile.php +++ b/vendor/guzzlehttp/psr7/src/UploadedFile.php @@ -11,15 +11,15 @@ class UploadedFile implements UploadedFileInterface { - private const ERRORS = [ - UPLOAD_ERR_OK, - UPLOAD_ERR_INI_SIZE, - UPLOAD_ERR_FORM_SIZE, - UPLOAD_ERR_PARTIAL, - UPLOAD_ERR_NO_FILE, - UPLOAD_ERR_NO_TMP_DIR, - UPLOAD_ERR_CANT_WRITE, - UPLOAD_ERR_EXTENSION, + private const ERROR_MAP = [ + UPLOAD_ERR_OK => 'UPLOAD_ERR_OK', + UPLOAD_ERR_INI_SIZE => 'UPLOAD_ERR_INI_SIZE', + UPLOAD_ERR_FORM_SIZE => 'UPLOAD_ERR_FORM_SIZE', + UPLOAD_ERR_PARTIAL => 'UPLOAD_ERR_PARTIAL', + UPLOAD_ERR_NO_FILE => 'UPLOAD_ERR_NO_FILE', + UPLOAD_ERR_NO_TMP_DIR => 'UPLOAD_ERR_NO_TMP_DIR', + UPLOAD_ERR_CANT_WRITE => 'UPLOAD_ERR_CANT_WRITE', + UPLOAD_ERR_EXTENSION => 'UPLOAD_ERR_EXTENSION', ]; /** @@ -104,7 +104,7 @@ private function setStreamOrFile($streamOrFile): void */ private function setError(int $error): void { - if (false === in_array($error, UploadedFile::ERRORS, true)) { + if (!isset(UploadedFile::ERROR_MAP[$error])) { throw new InvalidArgumentException( 'Invalid error status for UploadedFile' ); @@ -137,7 +137,7 @@ public function isMoved(): bool private function validateActive(): void { if (false === $this->isOk()) { - throw new RuntimeException('Cannot retrieve stream due to upload error'); + throw new RuntimeException(\sprintf('Cannot retrieve stream due to upload error (%s)', self::ERROR_MAP[$this->error])); } if ($this->isMoved()) { diff --git a/vendor/guzzlehttp/psr7/src/Uri.php b/vendor/guzzlehttp/psr7/src/Uri.php index 481dfca94..a7cdfb003 100644 --- a/vendor/guzzlehttp/psr7/src/Uri.php +++ b/vendor/guzzlehttp/psr7/src/Uri.php @@ -107,7 +107,7 @@ private static function parse(string $url) { // If IPv6 $prefix = ''; - if (preg_match('%^(.*://\[[0-9:a-f]+\])(.*?)$%', $url, $matches)) { + if (preg_match('%^(.*://\[[0-9:a-fA-F]+\])(.*?)$%', $url, $matches)) { /** @var array{0:string, 1:string, 2:string} $matches */ $prefix = $matches[1]; $url = $matches[2]; diff --git a/vendor/league/.DS_Store b/vendor/league/.DS_Store new file mode 100644 index 000000000..819112781 Binary files /dev/null and b/vendor/league/.DS_Store differ diff --git a/vendor/markbaker/.DS_Store b/vendor/markbaker/.DS_Store new file mode 100644 index 000000000..1bfe781f0 Binary files /dev/null and b/vendor/markbaker/.DS_Store differ diff --git a/vendor/myclabs/php-enum/LICENSE b/vendor/myclabs/php-enum/LICENSE deleted file mode 100644 index 2a8cf22ec..000000000 --- a/vendor/myclabs/php-enum/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 My C-Labs - -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. diff --git a/vendor/myclabs/php-enum/README.md b/vendor/myclabs/php-enum/README.md deleted file mode 100644 index 681d55ee7..000000000 --- a/vendor/myclabs/php-enum/README.md +++ /dev/null @@ -1,194 +0,0 @@ -# PHP Enum implementation inspired from SplEnum - -[![GitHub Actions][GA Image]][GA Link] -[![Latest Stable Version](https://poser.pugx.org/myclabs/php-enum/version.png)](https://packagist.org/packages/myclabs/php-enum) -[![Total Downloads](https://poser.pugx.org/myclabs/php-enum/downloads.png)](https://packagist.org/packages/myclabs/php-enum) -[![Psalm Shepherd][Shepherd Image]][Shepherd Link] - -Maintenance for this project is [supported via Tidelift](https://tidelift.com/subscription/pkg/packagist-myclabs-php-enum?utm_source=packagist-myclabs-php-enum&utm_medium=referral&utm_campaign=readme). - -## Why? - -First, and mainly, `SplEnum` is not integrated to PHP, you have to install the extension separately. - -Using an enum instead of class constants provides the following advantages: - -- You can use an enum as a parameter type: `function setAction(Action $action) {` -- You can use an enum as a return type: `function getAction() : Action {` -- You can enrich the enum with methods (e.g. `format`, `parse`, …) -- You can extend the enum to add new values (make your enum `final` to prevent it) -- You can get a list of all the possible values (see below) - -This Enum class is not intended to replace class constants, but only to be used when it makes sense. - -## Installation - -``` -composer require myclabs/php-enum -``` - -## Declaration - -```php -use MyCLabs\Enum\Enum; - -/** - * Action enum - */ -final class Action extends Enum -{ - private const VIEW = 'view'; - private const EDIT = 'edit'; -} -``` - -## Usage - -```php -$action = Action::VIEW(); - -// or with a dynamic key: -$action = Action::$key(); -// or with a dynamic value: -$action = Action::from($value); -// or -$action = new Action($value); -``` - -As you can see, static methods are automatically implemented to provide quick access to an enum value. - -One advantage over using class constants is to be able to use an enum as a parameter type: - -```php -function setAction(Action $action) { - // ... -} -``` - -## Documentation - -- `__construct()` The constructor checks that the value exist in the enum -- `__toString()` You can `echo $myValue`, it will display the enum value (value of the constant) -- `getValue()` Returns the current value of the enum -- `getKey()` Returns the key of the current value on Enum -- `equals()` Tests whether enum instances are equal (returns `true` if enum values are equal, `false` otherwise) - -Static methods: - -- `from()` Creates an Enum instance, checking that the value exist in the enum -- `toArray()` method Returns all possible values as an array (constant name in key, constant value in value) -- `keys()` Returns the names (keys) of all constants in the Enum class -- `values()` Returns instances of the Enum class of all Enum constants (constant name in key, Enum instance in value) -- `isValid()` Check if tested value is valid on enum set -- `isValidKey()` Check if tested key is valid on enum set -- `assertValidValue()` Assert the value is valid on enum set, throwing exception otherwise -- `search()` Return key for searched value - -### Static methods - -```php -final class Action extends Enum -{ - private const VIEW = 'view'; - private const EDIT = 'edit'; -} - -// Static method: -$action = Action::VIEW(); -$action = Action::EDIT(); -``` - -Static method helpers are implemented using [`__callStatic()`](http://www.php.net/manual/en/language.oop5.overloading.php#object.callstatic). - -If you care about IDE autocompletion, you can either implement the static methods yourself: - -```php -final class Action extends Enum -{ - private const VIEW = 'view'; - - /** - * @return Action - */ - public static function VIEW() { - return new Action(self::VIEW); - } -} -``` - -or you can use phpdoc (this is supported in PhpStorm for example): - -```php -/** - * @method static Action VIEW() - * @method static Action EDIT() - */ -final class Action extends Enum -{ - private const VIEW = 'view'; - private const EDIT = 'edit'; -} -``` - -## Native enums and migration -Native enum arrived to PHP in version 8.1: https://www.php.net/enumerations -If your project is running PHP 8.1+ or your library has it as a minimum requirement you should use it instead of this library. - -When migrating from `myclabs/php-enum`, the effort should be small if the usage was in the recommended way: -- private constants -- final classes -- no method overridden - -Changes for migration: -- Class definition should be changed from -```php -/** - * @method static Action VIEW() - * @method static Action EDIT() - */ -final class Action extends Enum -{ - private const VIEW = 'view'; - private const EDIT = 'edit'; -} -``` - to -```php -enum Action: string -{ - case VIEW = 'view'; - case EDIT = 'edit'; -} -``` -All places where the class was used as a type will continue to work. - -Usages and the change needed: - -| Operation | myclabs/php-enum | native enum | -|----------------------------------------------------------------|----------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Obtain an instance will change from | `$enumCase = Action::VIEW()` | `$enumCase = Action::VIEW` | -| Create an enum from a backed value | `$enumCase = new Action('view')` | `$enumCase = Action::from('view')` | -| Get the backed value of the enum instance | `$enumCase->getValue()` | `$enumCase->value` | -| Compare two enum instances | `$enumCase1 == $enumCase2`
                    or
                    `$enumCase1->equals($enumCase2)` | `$enumCase1 === $enumCase2` | -| Get the key/name of the enum instance | `$enumCase->getKey()` | `$enumCase->name` | -| Get a list of all the possible instances of the enum | `Action::values()` | `Action::cases()` | -| Get a map of possible instances of the enum mapped by name | `Action::values()` | `array_combine(array_map(fn($case) => $case->name, Action::cases()), Action::cases())`
                    or
                    `(new ReflectionEnum(Action::class))->getConstants()` | -| Get a list of all possible names of the enum | `Action::keys()` | `array_map(fn($case) => $case->name, Action::cases())` | -| Get a list of all possible backed values of the enum | `Action::toArray()` | `array_map(fn($case) => $case->value, Action::cases())` | -| Get a map of possible backed values of the enum mapped by name | `Action::toArray()` | `array_combine(array_map(fn($case) => $case->name, Action::cases()), array_map(fn($case) => $case->value, Action::cases()))`
                    or
                    `array_map(fn($case) => $case->value, (new ReflectionEnum(Action::class))->getConstants()))` | - -## Related projects - -- [PHP 8.1+ native enum](https://www.php.net/enumerations) -- [Doctrine enum mapping](https://github.com/acelaya/doctrine-enum-type) -- [Symfony ParamConverter integration](https://github.com/Ex3v/MyCLabsEnumParamConverter) -- [PHPStan integration](https://github.com/timeweb/phpstan-enum) - - -[GA Image]: https://github.com/myclabs/php-enum/workflows/CI/badge.svg - -[GA Link]: https://github.com/myclabs/php-enum/actions?query=workflow%3A%22CI%22+branch%3Amaster - -[Shepherd Image]: https://shepherd.dev/github/myclabs/php-enum/coverage.svg - -[Shepherd Link]: https://shepherd.dev/github/myclabs/php-enum diff --git a/vendor/myclabs/php-enum/SECURITY.md b/vendor/myclabs/php-enum/SECURITY.md deleted file mode 100644 index 84fd4e320..000000000 --- a/vendor/myclabs/php-enum/SECURITY.md +++ /dev/null @@ -1,11 +0,0 @@ -# Security Policy - -## Supported Versions - -Only the latest stable release is supported. - -## Reporting a Vulnerability - -To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). - -Tidelift will coordinate the fix and disclosure. diff --git a/vendor/myclabs/php-enum/composer.json b/vendor/myclabs/php-enum/composer.json deleted file mode 100644 index 978cb1959..000000000 --- a/vendor/myclabs/php-enum/composer.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "myclabs/php-enum", - "type": "library", - "description": "PHP Enum implementation", - "keywords": ["enum"], - "homepage": "http://github.com/myclabs/php-enum", - "license": "MIT", - "authors": [ - { - "name": "PHP Enum contributors", - "homepage": "https://github.com/myclabs/php-enum/graphs/contributors" - } - ], - "autoload": { - "psr-4": { - "MyCLabs\\Enum\\": "src/" - }, - "classmap": [ - "stubs/Stringable.php" - ] - }, - "autoload-dev": { - "psr-4": { - "MyCLabs\\Tests\\Enum\\": "tests/" - } - }, - "require": { - "php": "^7.3 || ^8.0", - "ext-json": "*" - }, - "require-dev": { - "phpunit/phpunit": "^9.5", - "squizlabs/php_codesniffer": "1.*", - "vimeo/psalm": "^4.6.2" - } -} diff --git a/vendor/myclabs/php-enum/src/Enum.php b/vendor/myclabs/php-enum/src/Enum.php deleted file mode 100644 index 4c94cf6d3..000000000 --- a/vendor/myclabs/php-enum/src/Enum.php +++ /dev/null @@ -1,318 +0,0 @@ - - * @author Daniel Costa - * @author Mirosław Filip - * - * @psalm-template T - * @psalm-immutable - * @psalm-consistent-constructor - */ -abstract class Enum implements \JsonSerializable, \Stringable -{ - /** - * Enum value - * - * @var mixed - * @psalm-var T - */ - protected $value; - - /** - * Enum key, the constant name - * - * @var string - */ - private $key; - - /** - * Store existing constants in a static cache per object. - * - * - * @var array - * @psalm-var array> - */ - protected static $cache = []; - - /** - * Cache of instances of the Enum class - * - * @var array - * @psalm-var array> - */ - protected static $instances = []; - - /** - * Creates a new value of some type - * - * @psalm-pure - * @param mixed $value - * - * @psalm-param T $value - * @throws \UnexpectedValueException if incompatible type is given. - */ - public function __construct($value) - { - if ($value instanceof static) { - /** @psalm-var T */ - $value = $value->getValue(); - } - - /** @psalm-suppress ImplicitToStringCast assertValidValueReturningKey returns always a string but psalm has currently an issue here */ - $this->key = static::assertValidValueReturningKey($value); - - /** @psalm-var T */ - $this->value = $value; - } - - /** - * This method exists only for the compatibility reason when deserializing a previously serialized version - * that didn't had the key property - */ - public function __wakeup() - { - /** @psalm-suppress DocblockTypeContradiction key can be null when deserializing an enum without the key */ - if ($this->key === null) { - /** - * @psalm-suppress InaccessibleProperty key is not readonly as marked by psalm - * @psalm-suppress PossiblyFalsePropertyAssignmentValue deserializing a case that was removed - */ - $this->key = static::search($this->value); - } - } - - /** - * @param mixed $value - * @return static - */ - public static function from($value): self - { - $key = static::assertValidValueReturningKey($value); - - return self::__callStatic($key, []); - } - - /** - * @psalm-pure - * @return mixed - * @psalm-return T - */ - public function getValue() - { - return $this->value; - } - - /** - * Returns the enum key (i.e. the constant name). - * - * @psalm-pure - * @return string - */ - public function getKey() - { - return $this->key; - } - - /** - * @psalm-pure - * @psalm-suppress InvalidCast - * @return string - */ - public function __toString() - { - return (string)$this->value; - } - - /** - * Determines if Enum should be considered equal with the variable passed as a parameter. - * Returns false if an argument is an object of different class or not an object. - * - * This method is final, for more information read https://github.com/myclabs/php-enum/issues/4 - * - * @psalm-pure - * @psalm-param mixed $variable - * @return bool - */ - final public function equals($variable = null): bool - { - return $variable instanceof self - && $this->getValue() === $variable->getValue() - && static::class === \get_class($variable); - } - - /** - * Returns the names (keys) of all constants in the Enum class - * - * @psalm-pure - * @psalm-return list - * @return array - */ - public static function keys() - { - return \array_keys(static::toArray()); - } - - /** - * Returns instances of the Enum class of all Enum constants - * - * @psalm-pure - * @psalm-return array - * @return static[] Constant name in key, Enum instance in value - */ - public static function values() - { - $values = array(); - - /** @psalm-var T $value */ - foreach (static::toArray() as $key => $value) { - $values[$key] = new static($value); - } - - return $values; - } - - /** - * Returns all possible values as an array - * - * @psalm-pure - * @psalm-suppress ImpureStaticProperty - * - * @psalm-return array - * @return array Constant name in key, constant value in value - */ - public static function toArray() - { - $class = static::class; - - if (!isset(static::$cache[$class])) { - /** @psalm-suppress ImpureMethodCall this reflection API usage has no side-effects here */ - $reflection = new \ReflectionClass($class); - /** @psalm-suppress ImpureMethodCall this reflection API usage has no side-effects here */ - static::$cache[$class] = $reflection->getConstants(); - } - - return static::$cache[$class]; - } - - /** - * Check if is valid enum value - * - * @param $value - * @psalm-param mixed $value - * @psalm-pure - * @psalm-assert-if-true T $value - * @return bool - */ - public static function isValid($value) - { - return \in_array($value, static::toArray(), true); - } - - /** - * Asserts valid enum value - * - * @psalm-pure - * @psalm-assert T $value - * @param mixed $value - */ - public static function assertValidValue($value): void - { - self::assertValidValueReturningKey($value); - } - - /** - * Asserts valid enum value - * - * @psalm-pure - * @psalm-assert T $value - * @param mixed $value - * @return string - */ - private static function assertValidValueReturningKey($value): string - { - if (false === ($key = static::search($value))) { - throw new \UnexpectedValueException("Value '$value' is not part of the enum " . static::class); - } - - return $key; - } - - /** - * Check if is valid enum key - * - * @param $key - * @psalm-param string $key - * @psalm-pure - * @return bool - */ - public static function isValidKey($key) - { - $array = static::toArray(); - - return isset($array[$key]) || \array_key_exists($key, $array); - } - - /** - * Return key for value - * - * @param mixed $value - * - * @psalm-param mixed $value - * @psalm-pure - * @return string|false - */ - public static function search($value) - { - return \array_search($value, static::toArray(), true); - } - - /** - * Returns a value when called statically like so: MyEnum::SOME_VALUE() given SOME_VALUE is a class constant - * - * @param string $name - * @param array $arguments - * - * @return static - * @throws \BadMethodCallException - * - * @psalm-pure - */ - public static function __callStatic($name, $arguments) - { - $class = static::class; - if (!isset(self::$instances[$class][$name])) { - $array = static::toArray(); - if (!isset($array[$name]) && !\array_key_exists($name, $array)) { - $message = "No static method or enum constant '$name' in class " . static::class; - throw new \BadMethodCallException($message); - } - return self::$instances[$class][$name] = new static($array[$name]); - } - return clone self::$instances[$class][$name]; - } - - /** - * Specify data which should be serialized to JSON. This method returns data that can be serialized by json_encode() - * natively. - * - * @return mixed - * @link http://php.net/manual/en/jsonserializable.jsonserialize.php - * @psalm-pure - */ - #[\ReturnTypeWillChange] - public function jsonSerialize() - { - return $this->getValue(); - } -} diff --git a/vendor/myclabs/php-enum/src/PHPUnit/Comparator.php b/vendor/myclabs/php-enum/src/PHPUnit/Comparator.php deleted file mode 100644 index 302bf80eb..000000000 --- a/vendor/myclabs/php-enum/src/PHPUnit/Comparator.php +++ /dev/null @@ -1,54 +0,0 @@ -register(new \MyCLabs\Enum\PHPUnit\Comparator()); - */ -final class Comparator extends \SebastianBergmann\Comparator\Comparator -{ - public function accepts($expected, $actual) - { - return $expected instanceof Enum && ( - $actual instanceof Enum || $actual === null - ); - } - - /** - * @param Enum $expected - * @param Enum|null $actual - * - * @return void - */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) - { - if ($expected->equals($actual)) { - return; - } - - throw new ComparisonFailure( - $expected, - $actual, - $this->formatEnum($expected), - $this->formatEnum($actual), - false, - 'Failed asserting that two Enums are equal.' - ); - } - - private function formatEnum(Enum $enum = null) - { - if ($enum === null) { - return "null"; - } - - return get_class($enum)."::{$enum->getKey()}()"; - } -} diff --git a/vendor/myclabs/php-enum/stubs/Stringable.php b/vendor/myclabs/php-enum/stubs/Stringable.php deleted file mode 100644 index 4811af700..000000000 --- a/vendor/myclabs/php-enum/stubs/Stringable.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. - */ - -namespace Carbon\PHPStan; - -use PHPStan\BetterReflection\Reflection; -use ReflectionMethod; - -if (!class_exists(AbstractReflectionMacro::class, false)) { - abstract class AbstractReflectionMacro extends AbstractMacro - { - /** - * {@inheritdoc} - */ - public function getReflection(): ?ReflectionMethod - { - if ($this->reflectionFunction instanceof Reflection\ReflectionMethod) { - return new Reflection\Adapter\ReflectionMethod($this->reflectionFunction); - } - - return $this->reflectionFunction instanceof ReflectionMethod - ? $this->reflectionFunction - : null; - } - } -} diff --git a/vendor/nesbot/carbon/lazy/Carbon/PHPStan/AbstractMacroStatic.php b/vendor/nesbot/carbon/lazy/Carbon/PHPStan/AbstractMacroStatic.php deleted file mode 100644 index bd4c8e804..000000000 --- a/vendor/nesbot/carbon/lazy/Carbon/PHPStan/AbstractMacroStatic.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Carbon\PHPStan; - -use PHPStan\BetterReflection\Reflection; -use ReflectionMethod; - -if (!class_exists(AbstractReflectionMacro::class, false)) { - abstract class AbstractReflectionMacro extends AbstractMacro - { - /** - * {@inheritdoc} - */ - public function getReflection(): ?Reflection\Adapter\ReflectionMethod - { - if ($this->reflectionFunction instanceof Reflection\Adapter\ReflectionMethod) { - return $this->reflectionFunction; - } - - if ($this->reflectionFunction instanceof Reflection\ReflectionMethod) { - return new Reflection\Adapter\ReflectionMethod($this->reflectionFunction); - } - - return $this->reflectionFunction instanceof ReflectionMethod - ? new Reflection\Adapter\ReflectionMethod( - Reflection\ReflectionMethod::createFromName( - $this->reflectionFunction->getDeclaringClass()->getName(), - $this->reflectionFunction->getName() - ) - ) - : null; - } - } -} diff --git a/vendor/nesbot/carbon/lazy/Carbon/PHPStan/MacroStrongType.php b/vendor/nesbot/carbon/lazy/Carbon/PHPStan/MacroStrongType.php deleted file mode 100644 index f615b3a64..000000000 --- a/vendor/nesbot/carbon/lazy/Carbon/PHPStan/MacroStrongType.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Carbon\PHPStan; - -if (!class_exists(LazyMacro::class, false)) { - abstract class LazyMacro extends AbstractReflectionMacro - { - /** - * {@inheritdoc} - */ - public function getFileName(): ?string - { - $file = $this->reflectionFunction->getFileName(); - - return (($file ? realpath($file) : null) ?: $file) ?: null; - } - - /** - * {@inheritdoc} - */ - public function getStartLine(): ?int - { - return $this->reflectionFunction->getStartLine(); - } - - /** - * {@inheritdoc} - */ - public function getEndLine(): ?int - { - return $this->reflectionFunction->getEndLine(); - } - } -} diff --git a/vendor/nesbot/carbon/lazy/Carbon/PHPStan/MacroWeakType.php b/vendor/nesbot/carbon/lazy/Carbon/PHPStan/MacroWeakType.php deleted file mode 100644 index bf64c1dd9..000000000 --- a/vendor/nesbot/carbon/lazy/Carbon/PHPStan/MacroWeakType.php +++ /dev/null @@ -1,51 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Carbon\PHPStan; - -if (!class_exists(LazyMacro::class, false)) { - abstract class LazyMacro extends AbstractReflectionMacro - { - /** - * {@inheritdoc} - * - * @return string|false - */ - public function getFileName() - { - $file = $this->reflectionFunction->getFileName(); - - return (($file ? realpath($file) : null) ?: $file) ?: null; - } - - /** - * {@inheritdoc} - * - * @return int|false - */ - public function getStartLine() - { - return $this->reflectionFunction->getStartLine(); - } - - /** - * {@inheritdoc} - * - * @return int|false - */ - public function getEndLine() - { - return $this->reflectionFunction->getEndLine(); - } - } -} diff --git a/vendor/nesbot/carbon/lazy/Carbon/ProtectedDatePeriod.php b/vendor/nesbot/carbon/lazy/Carbon/ProtectedDatePeriod.php new file mode 100644 index 000000000..927e48d21 --- /dev/null +++ b/vendor/nesbot/carbon/lazy/Carbon/ProtectedDatePeriod.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +use DatePeriod; + +if (!class_exists(DatePeriodBase::class, false)) { + class DatePeriodBase extends DatePeriod + { + /** + * Period start in PHP < 8.2. + * + * @var CarbonInterface + * + * @deprecated PHP 8.2 this property is no longer in sync with the actual period start. + */ + protected $start; + + /** + * Period end in PHP < 8.2. + * + * @var CarbonInterface|null + * + * @deprecated PHP 8.2 this property is no longer in sync with the actual period end. + */ + protected $end; + + /** + * Period current iterated date in PHP < 8.2. + * + * @var CarbonInterface|null + * + * @deprecated PHP 8.2 this property is no longer in sync with the actual period current iterated date. + */ + protected $current; + + /** + * Period interval in PHP < 8.2. + * + * @var CarbonInterval|null + * + * @deprecated PHP 8.2 this property is no longer in sync with the actual period interval. + */ + protected $interval; + + /** + * Period recurrences in PHP < 8.2. + * + * @var int|float|null + * + * @deprecated PHP 8.2 this property is no longer in sync with the actual period recurrences. + */ + protected $recurrences; + + /** + * Period start included option in PHP < 8.2. + * + * @var bool + * + * @deprecated PHP 8.2 this property is no longer in sync with the actual period start included option. + */ + protected $include_start_date; + } +} diff --git a/vendor/nesbot/carbon/lazy/Carbon/UnprotectedDatePeriod.php b/vendor/nesbot/carbon/lazy/Carbon/UnprotectedDatePeriod.php new file mode 100644 index 000000000..2341be715 --- /dev/null +++ b/vendor/nesbot/carbon/lazy/Carbon/UnprotectedDatePeriod.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +use DatePeriod; + +if (!class_exists(DatePeriodBase::class, false)) { + class DatePeriodBase extends DatePeriod + { + } +} diff --git a/vendor/nesbot/carbon/readme.md b/vendor/nesbot/carbon/readme.md index 97ec8ce08..76bc5c7d8 100644 --- a/vendor/nesbot/carbon/readme.md +++ b/vendor/nesbot/carbon/readme.md @@ -2,12 +2,15 @@ [![Latest Stable Version](https://img.shields.io/packagist/v/nesbot/carbon.svg?style=flat-square)](https://packagist.org/packages/nesbot/carbon) [![Total Downloads](https://img.shields.io/packagist/dt/nesbot/carbon.svg?style=flat-square)](https://packagist.org/packages/nesbot/carbon) -[![GitHub Actions](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fbriannesbitt%2FCarbon%2Fbadge&style=flat-square&label=Build&logo=none)](https://github.com/briannesbitt/Carbon/actions) -[![codecov.io](https://img.shields.io/codecov/c/github/briannesbitt/Carbon.svg?style=flat-square)](https://codecov.io/github/briannesbitt/Carbon?branch=master) -[![Tidelift](https://tidelift.com/badges/github/briannesbitt/Carbon)](https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme) +[![GitHub Actions](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2FCarbonPHP%2Fcarbon%2Fbadge&style=flat-square&label=Build&logo=none)](https://github.com/CarbonPHP/carbon/actions) +[![codecov.io](https://img.shields.io/codecov/c/github/CarbonPHP/carbon.svg?style=flat-square)](https://codecov.io/github/CarbonPHP/carbon/actions?branch=master) An international PHP extension for DateTime. [https://carbon.nesbot.com](https://carbon.nesbot.com) +> [!NOTE] +> We're migrating the repository from [briannesbitt/Carbon](https://github.com/briannesbitt/Carbon) to [CarbonPHP/carbon](https://github.com/CarbonPHP/carbon), +> which means if you're looking specific issues/pull-requests, you may have to search both. No other impact as code on both will be kept up to date. + ```php toDateTimeString()); printf("Right now in Vancouver is %s", Carbon::now('America/Vancouver')); //implicit __toString() $tomorrow = Carbon::now()->addDay(); $lastWeek = Carbon::now()->subWeek(); -$nextSummerOlympics = Carbon::createFromDate(2016)->addYears(4); $officialDate = Carbon::now()->toRfc2822String(); @@ -50,10 +52,15 @@ echo Carbon::parse('2019-07-23 14:51')->locale('fr_FR')->isoFormat('LLLL'); // ' // ... but also does 'from now', 'after' and 'before' // rolling up to seconds, minutes, hours, days, months, years -$daysSinceEpoch = Carbon::createFromTimestamp(0)->diffInDays(); -``` +$daysSinceEpoch = Carbon::createFromTimestamp(0)->diffInDays(); // something such as: + // 19817.6771 +$daysUntilInternetBlowUp = $internetWillBlowUpOn->diffInDays(); // Negative value since it's in the future: + // -5037.4560 -[Get supported nesbot/carbon with the Tidelift Subscription](https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme) +// Without parameter, difference is calculated from now, but doing $a->diff($b) +// it will count time from $a to $b. +Carbon::createFromTimestamp(0)->diffInDays($internetWillBlowUpOn); // 24855.1348 +``` ## Installation @@ -66,7 +73,7 @@ $ composer require nesbot/carbon ```json { "require": { - "nesbot/carbon": "^2.16" + "nesbot/carbon": "^3" } } ``` @@ -82,7 +89,7 @@ printf("Now: %s", Carbon::now()); ### Without Composer -Why are you not using [composer](https://getcomposer.org/)? Download the Carbon [latest release](https://github.com/briannesbitt/Carbon/releases) and put the contents of the ZIP archive into a directory in your project. Then require the file `autoload.php` to get all classes and dependencies loaded on need. +Why are you not using [composer](https://getcomposer.org/)? Download the Carbon [latest release](https://github.com/CarbonPHP/carbon/releases) and put the contents of the ZIP archive into a directory in your project. Then require the file `autoload.php` to get all classes and dependencies loaded on need. ```php
                    + ### Translators @@ -119,47 +126,55 @@ This project exists thanks to all the people who contribute. Support this project by becoming a sponsor. Your logo will show up here with a link to your website. - -Онлайн казино -CasinoHex Canada -Probukmacher -Casino-portugal.pt -Игровые автоматы -Slots City -inkedin -Онлайн казино України + +Online Kasyno Polis +Онлайн казино 777 +Best non Gamstop sites in the UK +Non GamStop Bookies UK +Route4Me Route Planner +gaia-wines.gr +Букмекер +best non Gamstop casinos +CasinoHex Canada +Real Money Pokies +Betking казино +WestNews онлайн казино Украины +UK casinos not on GamStop
                    See more OnlineCasinosSpelen -Best non Gamstop sites in the UK -Real Money Pokies -Non GamStop Bookies UK -Онлайн Казино Украины -SSSTwitter -Non-GamStop Bets UK -Chudovo -UK Casino Gap -NZ Casino Deps -NonStopCasino.org -Migliori Siti Non AAMS -UK NonGamStopCasinos -SnapTik -Proxidize -IG Downloader -Blastup -Organic Social Boost -AzuraCast -Triplebyte -GitHub Sponsors -Salesforce - - -[[Become a sponsor via OpenCollective](https://opencollective.com/Carbon#sponsor)] - - - - - - -[[Become a sponsor via GitHub](https://github.com/sponsors/kylekatarnls)] +Guidebook.BetWinner +Онлайн казино casino.ua +PayIDGambler +Legal Casino +Playfortune.net.br +https://play-fortune.pl/kasyno/z-minimalnym-depozytem/ +Best Betting +Slots not on GamStop +Sportsbook Reviews Online +Ставки на спорт Favbet +inkedin +Casino-portugal.pt +Probukmacher +Tidelift +Онлайн казино України +Sites not on GamStop +Лучшие онлайн казино Украины на Sportarena +Онлайн казино та їхні бонуси y-k.com.ua +Slots City +WildWinz Casino +Ігрові автомати +Non Gamstop Casinos +Slotozilla +casino non aams +Credit Zaim +ssddanbrown
                    + +[[See all](https://carbon.nesbot.com/#sponsors)] + +[[Become a sponsor via OpenCollective*](https://opencollective.com/Carbon#sponsor)] + +[[Become a sponsor via GitHub*](https://github.com/sponsors/kylekatarnls)] + +* This is a donation. No goods or services are expected in return. Any requests for refunds for those purposes will be rejected. ### Backers diff --git a/vendor/nesbot/carbon/sponsors.php b/vendor/nesbot/carbon/sponsors.php index 67b217161..05a6a919b 100644 --- a/vendor/nesbot/carbon/sponsors.php +++ b/vendor/nesbot/carbon/sponsors.php @@ -37,27 +37,87 @@ function getHtmlAttribute($rawValue): string function getOpenCollectiveSponsors(): string { - $customSponsorImages = [ + $customSponsorOverride = [ // For consistency and equity among sponsors, as of now, we kindly ask our sponsors // to provide an image having a width/height ratio between 1/1 and 2/1. // By default, we'll show the member picture from OpenCollective, and will resize it if bigger - // int(OpenCollective.MemberId) => ImageURL + 662698 => [ + // alt attribute + 'name' => 'Non Gamstop Casinos', + // title attribute + 'description' => 'Casinos not on Gamstop', + // src attribute + 'image' => 'https://lgcnews.com/wp-content/uploads/2018/01/LGC-logo-v8-temp.png', + // href attribute + 'website' => 'https://lgcnews.com/', + ], + 663069 => [ + // alt attribute + 'name' => 'Ставки на спорт Favbet', + // href attribute + 'website' => 'https://www.favbet.ua/uk/', + ], + 676798 => [ + // alt attribute + 'name' => 'Top Casinos Canada', + // title attribute + 'description' => 'Top Casinos Canada', + // src attribute + 'image' => 'https://topcasino.net/img/topcasino-logo-cover.png', + // href attribute + 'website' => 'https://topcasino.net/', + ], ]; $members = json_decode(file_get_contents('https://opencollective.com/carbon/members/all.json'), true); - $list = array_filter($members, static function ($member): bool { - return ($member['lastTransactionAmount'] > 3 || $member['isActive']) && - $member['role'] === 'BACKER' && - $member['type'] !== 'USER' && - ( - $member['totalAmountDonated'] > 100 || - $member['lastTransactionAt'] > CarbonImmutable::now() - ->subMonthsNoOverflow(getMaxHistoryMonthsByAmount($member['lastTransactionAmount'])) - ->format('Y-m-d h:i') || - $member['isActive'] && $member['lastTransactionAmount'] >= 30 - ); - }); + foreach ($members as &$member) { + $member = array_merge($member, $customSponsorOverride[$member['MemberId']] ?? []); + } + + // Adding sponsors paying via other payment methods + $members[] = [ + 'MemberId' => 1, + 'createdAt' => '2019-01-01 02:00', + 'type' => 'ORGANIZATION', + 'role' => 'BACKER', + 'tier' => 'backer+', + 'isActive' => true, + 'totalAmountDonated' => 1000, + 'currency' => 'USD', + 'lastTransactionAt' => CarbonImmutable::now()->format('Y-m-d').' 02:00', + 'lastTransactionAmount' => 25, + 'profile' => 'https://tidelift.com/', + 'name' => 'Tidelift', + 'description' => 'Get professional support for Carbon', + 'image' => 'https://carbon.nesbot.com/docs/sponsors/tidelift-brand.png', + 'website' => 'https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=docs', + ]; + $members[] = [ + 'MemberId' => 2, + 'createdAt' => '2024-11-14 02:00', + 'type' => 'ORGANIZATION', + 'role' => 'BACKER', + 'tier' => 'backer+ yearly', + 'isActive' => true, + 'totalAmountDonated' => 170, + 'currency' => 'USD', + 'lastTransactionAt' => '2024-11-14 02:00', + 'lastTransactionAmount' => 170, + 'profile' => 'https://www.slotozilla.com/nz/free-spins', + 'name' => 'Slotozilla', + 'description' => 'Slotozilla website', + 'image' => 'https://carbon.nesbot.com/docs/sponsors/slotozilla.png', + 'website' => 'https://www.slotozilla.com/nz/free-spins', + ]; + + $list = array_filter($members, static fn (array $member): bool => $member['totalAmountDonated'] > 3 && $member['role'] !== 'HOST' && ( + $member['totalAmountDonated'] > 100 || + $member['lastTransactionAt'] > CarbonImmutable::now() + ->subMonthsNoOverflow(getMaxHistoryMonthsByAmount($member['lastTransactionAmount'])) + ->format('Y-m-d h:i') || + $member['isActive'] && $member['lastTransactionAmount'] >= 30 + )); $list = array_map(static function (array $member): array { $createdAt = CarbonImmutable::parse($member['createdAt']); @@ -69,55 +129,134 @@ function getOpenCollectiveSponsors(): string ->modify($lastTransactionAt->format('H:i:s.u')); } - $monthlyContribution = (float) ($member['totalAmountDonated'] / ceil($createdAt->floatDiffInMonths())); + $isYearly = str_contains(strtolower($member['tier'] ?? ''), 'yearly'); + $monthlyContribution = (float) ( + ($isYearly && $lastTransactionAt > CarbonImmutable::parse('-1 year')) + ? ($member['lastTransactionAmount'] / 11.2) // 11.2 instead of 12 to include the discount for yearly subscription + : ($member['totalAmountDonated'] / ceil($createdAt->floatDiffInMonths())) + ); - if ( - $lastTransactionAt->isAfter('last month') && - $member['lastTransactionAmount'] > $monthlyContribution - ) { - $monthlyContribution = (float) $member['lastTransactionAmount']; + if (!$isYearly) { + if ( + $lastTransactionAt->isAfter('last month') && + $member['lastTransactionAmount'] > $monthlyContribution + ) { + $monthlyContribution = (float) $member['lastTransactionAmount']; + } + + if ($lastTransactionAt->isBefore('-75 days')) { + $days = min(120, $lastTransactionAt->diffInDays('now') - 70); + $monthlyContribution *= 1 - $days / 240; + } } - $yearlyContribution = (float) ($member['totalAmountDonated'] / max(1, $createdAt->floatDiffInYears())); + $yearlyContribution = (float) ( + $isYearly + ? (12 * $monthlyContribution) + : ($member['totalAmountDonated'] / max(1, $createdAt->floatDiffInYears())) + ); $status = null; + $rank = 0; - if ($monthlyContribution > 29) { + if ($monthlyContribution > 50 || $yearlyContribution > 900) { + $status = 'sponsor'; + $rank = 5; + } elseif ($monthlyContribution > 29 || $yearlyContribution > 700) { $status = 'sponsor'; - } elseif ($monthlyContribution > 4.5 || $yearlyContribution > 29) { + $rank = 4; + } elseif ($monthlyContribution > 14.5 || $yearlyContribution > 500) { + $status = 'backerPlus'; + $rank = 3; + } elseif ($monthlyContribution > 4.5 || $yearlyContribution > 80) { $status = 'backer'; + $rank = 2; } elseif ($member['totalAmountDonated'] > 0) { $status = 'helper'; + $rank = 1; } return array_merge($member, [ - 'star' => ($monthlyContribution > 98 || $yearlyContribution > 500), + 'star' => ($monthlyContribution > 98 || $yearlyContribution > 800), 'status' => $status, + 'rank' => $rank, 'monthlyContribution' => $monthlyContribution, 'yearlyContribution' => $yearlyContribution, ]); }, $list); usort($list, static function (array $a, array $b): int { - return ($b['monthlyContribution'] <=> $a['monthlyContribution']) + return ($b['star'] <=> $a['star']) + ?: ($b['rank'] <=> $a['rank']) + ?: ($b['monthlyContribution'] <=> $a['monthlyContribution']) ?: ($b['totalAmountDonated'] <=> $a['totalAmountDonated']); }); - return implode('', array_map(static function (array $member) use ($customSponsorImages): string { - $href = htmlspecialchars($member['website'] ?? $member['profile']); + $membersByUrl = []; + $output = ''; + $extra = ''; + + foreach ($list as $member) { + $url = $member['website'] ?? $member['profile']; + + if (isset($membersByUrl[$url]) || !\in_array($member['status'], ['sponsor', 'backerPlus'], true)) { + continue; + } + + $membersByUrl[$url] = $member; + $href = htmlspecialchars($url); $src = $customSponsorImages[$member['MemberId'] ?? ''] ?? $member['image'] ?? (strtr($member['profile'], ['https://opencollective.com/' => 'https://images.opencollective.com/']).'/avatar/256.png'); [$x, $y] = @getimagesize($src) ?: [0, 0]; $validImage = ($x && $y); $src = $validImage ? htmlspecialchars($src) : 'https://opencollective.com/static/images/default-guest-logo.svg'; - $height = $member['status'] === 'sponsor' ? 64 : 42; + $height = match ($member['status']) { + 'sponsor' => 64, + 'backerPlus' => 42, + 'backer' => 32, + default => 24, + }; + $rel = match ($member['status']) { + 'sponsor', 'backerPlus' => '', + default => ' rel="sponsored"', + }; + $width = min($height * 2, $validImage ? round($x * $height / $y) : $height); - $href .= (strpos($href, '?') === false ? '?' : '&').'utm_source=opencollective&utm_medium=github&utm_campaign=Carbon'; + + if (!str_contains($href, 'utm_source') && !preg_match('/^https?:\/\/(?:www\.)?(?:onlinekasyno-polis\.pl|zonaminecraft\.net|slotozilla\.com)(\/.*)?/', $href)) { + $href .= (!str_contains($href, '?') ? '?' : '&').'utm_source=opencollective&utm_medium=github&utm_campaign=Carbon'; + } + $title = getHtmlAttribute(($member['description'] ?? null) ?: $member['name']); $alt = getHtmlAttribute($member['name']); - return "\n".''. + if ($member['star']) { + $width *= 1.5; + $height *= 1.5; + } + + $link = "\n".''. ''.$alt.''. ''; - }, $list))."\n"; + + if ($member['rank'] >= 5) { + $output .= $link; + + continue; + } + + $extra .= $link; + } + + $github = [ + 8343178 => 'ssddanbrown', + ]; + + foreach ($github as $avatar => $user) { + $extra .= "\n".''. + ''.$user.''. + ''; + } + + return $output.'
                    See more'.$extra.'
                    '; } file_put_contents('readme.md', preg_replace_callback( @@ -125,5 +264,5 @@ function getOpenCollectiveSponsors(): string static function (array $match): string { return $match[1].getOpenCollectiveSponsors().$match[2]; }, - file_get_contents('readme.md') + file_get_contents('readme.md'), )); diff --git a/vendor/nesbot/carbon/src/Carbon/AbstractTranslator.php b/vendor/nesbot/carbon/src/Carbon/AbstractTranslator.php index 8b8fe089e..8dd47aeed 100644 --- a/vendor/nesbot/carbon/src/Carbon/AbstractTranslator.php +++ b/vendor/nesbot/carbon/src/Carbon/AbstractTranslator.php @@ -1,5 +1,7 @@ */ - protected $aliases = [ + protected array $aliases = [ 'me' => 'sr_Latn_ME', 'scr' => 'sh', ]; @@ -66,32 +68,31 @@ abstract class AbstractTranslator extends Translation\Translator * * @return static */ - public static function get($locale = null) + public static function get(?string $locale = null): static { $locale = $locale ?: 'en'; $key = static::class === Translator::class ? $locale : static::class.'|'.$locale; + $count = \count(static::$singletons); - if (!isset(static::$singletons[$key])) { - static::$singletons[$key] = new static($locale); + // Remember only the last 10 translators created + if ($count > 10) { + foreach (\array_slice(array_keys(static::$singletons), 0, $count - 10) as $index) { + unset(static::$singletons[$index]); + } } + static::$singletons[$key] ??= new static($locale); + return static::$singletons[$key]; } - public function __construct($locale, MessageFormatterInterface $formatter = null, $cacheDir = null, $debug = false) + public function __construct($locale, ?MessageFormatterInterface $formatter = null, $cacheDir = null, $debug = false) { - parent::setLocale($locale); - $this->initializing = true; - $this->directories = [__DIR__.'/Lang']; - $this->addLoader('array', new ArrayLoader()); - parent::__construct($locale, new MessageFormatterMapper($formatter), $cacheDir, $debug); - $this->initializing = false; + $this->initialize($locale, $formatter, $cacheDir, $debug); } /** * Returns the list of directories translation files are searched in. - * - * @return array */ public function getDirectories(): array { @@ -105,7 +106,7 @@ public function getDirectories(): array * * @return $this */ - public function setDirectories(array $directories) + public function setDirectories(array $directories): static { $this->directories = $directories; @@ -119,7 +120,7 @@ public function setDirectories(array $directories) * * @return $this */ - public function addDirectory(string $directory) + public function addDirectory(string $directory): static { $this->directories[] = $directory; @@ -133,25 +134,22 @@ public function addDirectory(string $directory) * * @return $this */ - public function removeDirectory(string $directory) + public function removeDirectory(string $directory): static { $search = rtrim(strtr($directory, '\\', '/'), '/'); - return $this->setDirectories(array_filter($this->getDirectories(), function ($item) use ($search) { - return rtrim(strtr($item, '\\', '/'), '/') !== $search; - })); + return $this->setDirectories(array_filter( + $this->getDirectories(), + static fn ($item) => rtrim(strtr($item, '\\', '/'), '/') !== $search, + )); } /** * Reset messages of a locale (all locale if no locale passed). * Remove custom messages and reload initial messages from matching * file in Lang directory. - * - * @param string|null $locale - * - * @return bool */ - public function resetMessages($locale = null) + public function resetMessages(?string $locale = null): bool { if ($locale === null) { $this->messages = []; @@ -159,8 +157,10 @@ public function resetMessages($locale = null) return true; } + $this->assertValidLocale($locale); + foreach ($this->getDirectories() as $directory) { - $data = @include sprintf('%s/%s.php', rtrim($directory, '\\/'), $locale); + $data = @include \sprintf('%s/%s.php', rtrim($directory, '\\/'), $locale); if ($data !== false) { $this->messages[$locale] = $data; @@ -180,7 +180,7 @@ public function resetMessages($locale = null) * * @return array */ - public function getLocalesFiles($prefix = '') + public function getLocalesFiles(string $prefix = ''): array { $files = []; @@ -203,7 +203,7 @@ public function getLocalesFiles($prefix = '') * * @return array */ - public function getAvailableLocales($prefix = '') + public function getAvailableLocales(string $prefix = ''): array { $locales = []; foreach ($this->getLocalesFiles($prefix) as $file) { @@ -228,7 +228,7 @@ protected function translate(?string $id, array $parameters = [], ?string $domai // @codeCoverageIgnoreStart try { $count = (new ReflectionFunction($format))->getNumberOfRequiredParameters(); - } catch (ReflectionException $exception) { + } catch (ReflectionException) { $count = 0; } // @codeCoverageIgnoreEnd @@ -249,7 +249,7 @@ protected function translate(?string $id, array $parameters = [], ?string $domai * * @return bool */ - protected function loadMessagesFromFile($locale) + protected function loadMessagesFromFile(string $locale): bool { return isset($this->messages[$locale]) || $this->resetMessages($locale); } @@ -262,7 +262,7 @@ protected function loadMessagesFromFile($locale) * * @return $this */ - public function setMessages($locale, $messages) + public function setMessages(string $locale, array $messages): static { $this->loadMessagesFromFile($locale); $this->addResource('array', $messages, $locale); @@ -281,7 +281,7 @@ public function setMessages($locale, $messages) * * @return $this */ - public function setTranslations($messages) + public function setTranslations(array $messages): static { return $this->setMessages($this->getLocale(), $messages); } @@ -289,12 +289,8 @@ public function setTranslations($messages) /** * Get messages of a locale, if none given, return all the * languages. - * - * @param string|null $locale - * - * @return array */ - public function getMessages($locale = null) + public function getMessages(?string $locale = null): array { return $locale === null ? $this->messages : $this->messages[$locale]; } @@ -303,16 +299,14 @@ public function getMessages($locale = null) * Set the current translator locale and indicate if the source locale file exists * * @param string $locale locale ex. en - * - * @return bool */ - public function setLocale($locale) + public function setLocale($locale): void { $locale = preg_replace_callback('/[-_]([a-z]{2,}|\d{2,})/', function ($matches) { // _2-letters or YUE is a region, _3+-letters is a variant $upper = strtoupper($matches[1]); - if ($upper === 'YUE' || $upper === 'ISO' || \strlen($upper) < 3) { + if ($upper === 'YUE' || $upper === 'ISO' || \strlen($upper) <= static::REGION_CODE_LENGTH) { return "_$upper"; } @@ -322,7 +316,7 @@ public function setLocale($locale) $previousLocale = $this->getLocale(); if ($previousLocale === $locale && isset($this->messages[$locale])) { - return true; + return; } unset(static::$singletons[$previousLocale]); @@ -334,13 +328,12 @@ public function setLocale($locale) $completeLocaleChunks = preg_split('/[_.-]+/', $completeLocale); - $getScore = function ($language) use ($completeLocaleChunks) { - return self::compareChunkLists($completeLocaleChunks, preg_split('/[_.-]+/', $language)); - }; + $getScore = static fn ($language) => self::compareChunkLists( + $completeLocaleChunks, + preg_split('/[_.-]+/', $language), + ); - usort($locales, function ($first, $second) use ($getScore) { - return $getScore($second) <=> $getScore($first); - }); + usort($locales, static fn ($first, $second) => $getScore($second) <=> $getScore($first)); $locale = $locales[0]; } @@ -357,12 +350,10 @@ public function setLocale($locale) } if (!$this->loadMessagesFromFile($locale) && !$this->initializing) { - return false; + return; } parent::setLocale($locale); - - return true; } /** @@ -377,6 +368,28 @@ public function __debugInfo() ]; } + public function __serialize(): array + { + return [ + 'locale' => $this->getLocale(), + ]; + } + + public function __unserialize(array $data): void + { + $this->initialize($data['locale'] ?? 'en'); + } + + private function initialize($locale, ?MessageFormatterInterface $formatter = null, $cacheDir = null, $debug = false): void + { + parent::setLocale($locale); + $this->initializing = true; + $this->directories = [__DIR__.'/Lang']; + $this->addLoader('array', new ArrayLoader()); + parent::__construct($locale, new MessageFormatterMapper($formatter), $cacheDir, $debug); + $this->initializing = false; + } + private static function compareChunkLists($referenceChunks, $chunks) { $score = 0; diff --git a/vendor/nesbot/carbon/src/Carbon/Callback.php b/vendor/nesbot/carbon/src/Carbon/Callback.php new file mode 100644 index 000000000..c7456f1eb --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Callback.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +use Closure; +use DateInterval; +use DatePeriod; +use DateTime; +use DateTimeInterface; +use DateTimeZone; +use ReflectionFunction; +use ReflectionNamedType; +use ReflectionType; + +final class Callback +{ + private ?ReflectionFunction $function; + + private function __construct(private readonly Closure $closure) + { + } + + public static function fromClosure(Closure $closure): self + { + return new self($closure); + } + + public static function parameter(mixed $closure, mixed $value, string|int $index = 0): mixed + { + if ($closure instanceof Closure) { + return self::fromClosure($closure)->prepareParameter($value, $index); + } + + return $value; + } + + public function getReflectionFunction(): ReflectionFunction + { + return $this->function ??= new ReflectionFunction($this->closure); + } + + public function prepareParameter(mixed $value, string|int $index = 0): mixed + { + $type = $this->getParameterType($index); + + if (!($type instanceof ReflectionNamedType)) { + return $value; + } + + $name = $type->getName(); + + if ($name === CarbonInterface::class) { + $name = $value instanceof DateTime ? Carbon::class : CarbonImmutable::class; + } + + if (!class_exists($name) || is_a($value, $name)) { + return $value; + } + + $class = $this->getPromotedClass($value); + + if ($class && is_a($name, $class, true)) { + return $name::instance($value); + } + + return $value; + } + + public function call(mixed ...$arguments): mixed + { + foreach ($arguments as $index => &$value) { + if ($this->getPromotedClass($value)) { + $value = $this->prepareParameter($value, $index); + } + } + + return ($this->closure)(...$arguments); + } + + private function getParameterType(string|int $index): ?ReflectionType + { + $parameters = $this->getReflectionFunction()->getParameters(); + + if (\is_int($index)) { + return ($parameters[$index] ?? null)?->getType(); + } + + foreach ($parameters as $parameter) { + if ($parameter->getName() === $index) { + return $parameter->getType(); + } + } + + return null; + } + + /** @return class-string|null */ + private function getPromotedClass(mixed $value): ?string + { + if ($value instanceof DateTimeInterface) { + return CarbonInterface::class; + } + + if ($value instanceof DateInterval) { + return CarbonInterval::class; + } + + if ($value instanceof DatePeriod) { + return CarbonPeriod::class; + } + + if ($value instanceof DateTimeZone) { + return CarbonTimeZone::class; + } + + return null; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Carbon.php b/vendor/nesbot/carbon/src/Carbon/Carbon.php index e32569ae3..8364e8412 100644 --- a/vendor/nesbot/carbon/src/Carbon/Carbon.php +++ b/vendor/nesbot/carbon/src/Carbon/Carbon.php @@ -1,5 +1,7 @@ * - * @property int $year - * @property int $yearIso - * @property int $month - * @property int $day - * @property int $hour - * @property int $minute - * @property int $second - * @property int $micro - * @property int $microsecond - * @property int|float|string $timestamp seconds since the Unix Epoch - * @property string $englishDayOfWeek the day of week in English - * @property string $shortEnglishDayOfWeek the abbreviated day of week in English - * @property string $englishMonth the month in English - * @property string $shortEnglishMonth the abbreviated month in English - * @property int $milliseconds - * @property int $millisecond - * @property int $milli - * @property int $week 1 through 53 - * @property int $isoWeek 1 through 53 - * @property int $weekYear year according to week format - * @property int $isoWeekYear year according to ISO week format - * @property int $dayOfYear 1 through 366 - * @property int $age does a diffInYears() with default parameters - * @property int $offset the timezone offset in seconds from UTC - * @property int $offsetMinutes the timezone offset in minutes from UTC - * @property int $offsetHours the timezone offset in hours from UTC - * @property CarbonTimeZone $timezone the current timezone - * @property CarbonTimeZone $tz alias of $timezone - * @property-read int $dayOfWeek 0 (for Sunday) through 6 (for Saturday) - * @property-read int $dayOfWeekIso 1 (for Monday) through 7 (for Sunday) - * @property-read int $weekOfYear ISO-8601 week number of year, weeks starting on Monday - * @property-read int $daysInMonth number of days in the given month - * @property-read string $latinMeridiem "am"/"pm" (Ante meridiem or Post meridiem latin lowercase mark) - * @property-read string $latinUpperMeridiem "AM"/"PM" (Ante meridiem or Post meridiem latin uppercase mark) - * @property-read string $timezoneAbbreviatedName the current timezone abbreviated name - * @property-read string $tzAbbrName alias of $timezoneAbbreviatedName - * @property-read string $dayName long name of weekday translated according to Carbon locale, in english if no translation available for current language - * @property-read string $shortDayName short name of weekday translated according to Carbon locale, in english if no translation available for current language - * @property-read string $minDayName very short name of weekday translated according to Carbon locale, in english if no translation available for current language - * @property-read string $monthName long name of month translated according to Carbon locale, in english if no translation available for current language - * @property-read string $shortMonthName short name of month translated according to Carbon locale, in english if no translation available for current language - * @property-read string $meridiem lowercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language - * @property-read string $upperMeridiem uppercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language - * @property-read int $noZeroHour current hour from 1 to 24 - * @property-read int $weeksInYear 51 through 53 - * @property-read int $isoWeeksInYear 51 through 53 - * @property-read int $weekOfMonth 1 through 5 - * @property-read int $weekNumberInMonth 1 through 5 - * @property-read int $firstWeekDay 0 through 6 - * @property-read int $lastWeekDay 0 through 6 - * @property-read int $daysInYear 365 or 366 - * @property-read int $quarter the quarter of this instance, 1 - 4 - * @property-read int $decade the decade of this instance - * @property-read int $century the century of this instance - * @property-read int $millennium the millennium of this instance - * @property-read bool $dst daylight savings time indicator, true if DST, false otherwise - * @property-read bool $local checks if the timezone is local, true if local, false otherwise - * @property-read bool $utc checks if the timezone is UTC, true if UTC, false otherwise - * @property-read string $timezoneName the current timezone name - * @property-read string $tzName alias of $timezoneName - * @property-read string $locale locale of the current instance + * @property string $localeDayOfWeek the day of week in current locale + * @property string $shortLocaleDayOfWeek the abbreviated day of week in current locale + * @property string $localeMonth the month in current locale + * @property string $shortLocaleMonth the abbreviated month in current locale + * @property int $year + * @property int $yearIso + * @property int $month + * @property int $day + * @property int $hour + * @property int $minute + * @property int $second + * @property int $micro + * @property int $microsecond + * @property int $dayOfWeekIso 1 (for Monday) through 7 (for Sunday) + * @property int|float|string $timestamp seconds since the Unix Epoch + * @property string $englishDayOfWeek the day of week in English + * @property string $shortEnglishDayOfWeek the abbreviated day of week in English + * @property string $englishMonth the month in English + * @property string $shortEnglishMonth the abbreviated month in English + * @property int $milliseconds + * @property int $millisecond + * @property int $milli + * @property int $week 1 through 53 + * @property int $isoWeek 1 through 53 + * @property int $weekYear year according to week format + * @property int $isoWeekYear year according to ISO week format + * @property int $age does a diffInYears() with default parameters + * @property int $offset the timezone offset in seconds from UTC + * @property int $offsetMinutes the timezone offset in minutes from UTC + * @property int $offsetHours the timezone offset in hours from UTC + * @property CarbonTimeZone $timezone the current timezone + * @property CarbonTimeZone $tz alias of $timezone + * @property int $centuryOfMillennium The value of the century starting from the beginning of the current millennium + * @property int $dayOfCentury The value of the day starting from the beginning of the current century + * @property int $dayOfDecade The value of the day starting from the beginning of the current decade + * @property int $dayOfMillennium The value of the day starting from the beginning of the current millennium + * @property int $dayOfMonth The value of the day starting from the beginning of the current month + * @property int $dayOfQuarter The value of the day starting from the beginning of the current quarter + * @property int $dayOfWeek 0 (for Sunday) through 6 (for Saturday) + * @property int $dayOfYear 1 through 366 + * @property int $decadeOfCentury The value of the decade starting from the beginning of the current century + * @property int $decadeOfMillennium The value of the decade starting from the beginning of the current millennium + * @property int $hourOfCentury The value of the hour starting from the beginning of the current century + * @property int $hourOfDay The value of the hour starting from the beginning of the current day + * @property int $hourOfDecade The value of the hour starting from the beginning of the current decade + * @property int $hourOfMillennium The value of the hour starting from the beginning of the current millennium + * @property int $hourOfMonth The value of the hour starting from the beginning of the current month + * @property int $hourOfQuarter The value of the hour starting from the beginning of the current quarter + * @property int $hourOfWeek The value of the hour starting from the beginning of the current week + * @property int $hourOfYear The value of the hour starting from the beginning of the current year + * @property int $microsecondOfCentury The value of the microsecond starting from the beginning of the current century + * @property int $microsecondOfDay The value of the microsecond starting from the beginning of the current day + * @property int $microsecondOfDecade The value of the microsecond starting from the beginning of the current decade + * @property int $microsecondOfHour The value of the microsecond starting from the beginning of the current hour + * @property int $microsecondOfMillennium The value of the microsecond starting from the beginning of the current millennium + * @property int $microsecondOfMillisecond The value of the microsecond starting from the beginning of the current millisecond + * @property int $microsecondOfMinute The value of the microsecond starting from the beginning of the current minute + * @property int $microsecondOfMonth The value of the microsecond starting from the beginning of the current month + * @property int $microsecondOfQuarter The value of the microsecond starting from the beginning of the current quarter + * @property int $microsecondOfSecond The value of the microsecond starting from the beginning of the current second + * @property int $microsecondOfWeek The value of the microsecond starting from the beginning of the current week + * @property int $microsecondOfYear The value of the microsecond starting from the beginning of the current year + * @property int $millisecondOfCentury The value of the millisecond starting from the beginning of the current century + * @property int $millisecondOfDay The value of the millisecond starting from the beginning of the current day + * @property int $millisecondOfDecade The value of the millisecond starting from the beginning of the current decade + * @property int $millisecondOfHour The value of the millisecond starting from the beginning of the current hour + * @property int $millisecondOfMillennium The value of the millisecond starting from the beginning of the current millennium + * @property int $millisecondOfMinute The value of the millisecond starting from the beginning of the current minute + * @property int $millisecondOfMonth The value of the millisecond starting from the beginning of the current month + * @property int $millisecondOfQuarter The value of the millisecond starting from the beginning of the current quarter + * @property int $millisecondOfSecond The value of the millisecond starting from the beginning of the current second + * @property int $millisecondOfWeek The value of the millisecond starting from the beginning of the current week + * @property int $millisecondOfYear The value of the millisecond starting from the beginning of the current year + * @property int $minuteOfCentury The value of the minute starting from the beginning of the current century + * @property int $minuteOfDay The value of the minute starting from the beginning of the current day + * @property int $minuteOfDecade The value of the minute starting from the beginning of the current decade + * @property int $minuteOfHour The value of the minute starting from the beginning of the current hour + * @property int $minuteOfMillennium The value of the minute starting from the beginning of the current millennium + * @property int $minuteOfMonth The value of the minute starting from the beginning of the current month + * @property int $minuteOfQuarter The value of the minute starting from the beginning of the current quarter + * @property int $minuteOfWeek The value of the minute starting from the beginning of the current week + * @property int $minuteOfYear The value of the minute starting from the beginning of the current year + * @property int $monthOfCentury The value of the month starting from the beginning of the current century + * @property int $monthOfDecade The value of the month starting from the beginning of the current decade + * @property int $monthOfMillennium The value of the month starting from the beginning of the current millennium + * @property int $monthOfQuarter The value of the month starting from the beginning of the current quarter + * @property int $monthOfYear The value of the month starting from the beginning of the current year + * @property int $quarterOfCentury The value of the quarter starting from the beginning of the current century + * @property int $quarterOfDecade The value of the quarter starting from the beginning of the current decade + * @property int $quarterOfMillennium The value of the quarter starting from the beginning of the current millennium + * @property int $quarterOfYear The value of the quarter starting from the beginning of the current year + * @property int $secondOfCentury The value of the second starting from the beginning of the current century + * @property int $secondOfDay The value of the second starting from the beginning of the current day + * @property int $secondOfDecade The value of the second starting from the beginning of the current decade + * @property int $secondOfHour The value of the second starting from the beginning of the current hour + * @property int $secondOfMillennium The value of the second starting from the beginning of the current millennium + * @property int $secondOfMinute The value of the second starting from the beginning of the current minute + * @property int $secondOfMonth The value of the second starting from the beginning of the current month + * @property int $secondOfQuarter The value of the second starting from the beginning of the current quarter + * @property int $secondOfWeek The value of the second starting from the beginning of the current week + * @property int $secondOfYear The value of the second starting from the beginning of the current year + * @property int $weekOfCentury The value of the week starting from the beginning of the current century + * @property int $weekOfDecade The value of the week starting from the beginning of the current decade + * @property int $weekOfMillennium The value of the week starting from the beginning of the current millennium + * @property int $weekOfMonth 1 through 5 + * @property int $weekOfQuarter The value of the week starting from the beginning of the current quarter + * @property int $weekOfYear ISO-8601 week number of year, weeks starting on Monday + * @property int $yearOfCentury The value of the year starting from the beginning of the current century + * @property int $yearOfDecade The value of the year starting from the beginning of the current decade + * @property int $yearOfMillennium The value of the year starting from the beginning of the current millennium + * @property-read string $latinMeridiem "am"/"pm" (Ante meridiem or Post meridiem latin lowercase mark) + * @property-read string $latinUpperMeridiem "AM"/"PM" (Ante meridiem or Post meridiem latin uppercase mark) + * @property-read string $timezoneAbbreviatedName the current timezone abbreviated name + * @property-read string $tzAbbrName alias of $timezoneAbbreviatedName + * @property-read string $dayName long name of weekday translated according to Carbon locale, in english if no translation available for current language + * @property-read string $shortDayName short name of weekday translated according to Carbon locale, in english if no translation available for current language + * @property-read string $minDayName very short name of weekday translated according to Carbon locale, in english if no translation available for current language + * @property-read string $monthName long name of month translated according to Carbon locale, in english if no translation available for current language + * @property-read string $shortMonthName short name of month translated according to Carbon locale, in english if no translation available for current language + * @property-read string $meridiem lowercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language + * @property-read string $upperMeridiem uppercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language + * @property-read int $noZeroHour current hour from 1 to 24 + * @property-read int $isoWeeksInYear 51 through 53 + * @property-read int $weekNumberInMonth 1 through 5 + * @property-read int $firstWeekDay 0 through 6 + * @property-read int $lastWeekDay 0 through 6 + * @property-read int $quarter the quarter of this instance, 1 - 4 + * @property-read int $decade the decade of this instance + * @property-read int $century the century of this instance + * @property-read int $millennium the millennium of this instance + * @property-read bool $dst daylight savings time indicator, true if DST, false otherwise + * @property-read bool $local checks if the timezone is local, true if local, false otherwise + * @property-read bool $utc checks if the timezone is UTC, true if UTC, false otherwise + * @property-read string $timezoneName the current timezone name + * @property-read string $tzName alias of $timezoneName + * @property-read string $locale locale of the current instance + * @property-read int $centuriesInMillennium The number of centuries contained in the current millennium + * @property-read int $daysInCentury The number of days contained in the current century + * @property-read int $daysInDecade The number of days contained in the current decade + * @property-read int $daysInMillennium The number of days contained in the current millennium + * @property-read int $daysInMonth number of days in the given month + * @property-read int $daysInQuarter The number of days contained in the current quarter + * @property-read int $daysInWeek The number of days contained in the current week + * @property-read int $daysInYear 365 or 366 + * @property-read int $decadesInCentury The number of decades contained in the current century + * @property-read int $decadesInMillennium The number of decades contained in the current millennium + * @property-read int $hoursInCentury The number of hours contained in the current century + * @property-read int $hoursInDay The number of hours contained in the current day + * @property-read int $hoursInDecade The number of hours contained in the current decade + * @property-read int $hoursInMillennium The number of hours contained in the current millennium + * @property-read int $hoursInMonth The number of hours contained in the current month + * @property-read int $hoursInQuarter The number of hours contained in the current quarter + * @property-read int $hoursInWeek The number of hours contained in the current week + * @property-read int $hoursInYear The number of hours contained in the current year + * @property-read int $microsecondsInCentury The number of microseconds contained in the current century + * @property-read int $microsecondsInDay The number of microseconds contained in the current day + * @property-read int $microsecondsInDecade The number of microseconds contained in the current decade + * @property-read int $microsecondsInHour The number of microseconds contained in the current hour + * @property-read int $microsecondsInMillennium The number of microseconds contained in the current millennium + * @property-read int $microsecondsInMillisecond The number of microseconds contained in the current millisecond + * @property-read int $microsecondsInMinute The number of microseconds contained in the current minute + * @property-read int $microsecondsInMonth The number of microseconds contained in the current month + * @property-read int $microsecondsInQuarter The number of microseconds contained in the current quarter + * @property-read int $microsecondsInSecond The number of microseconds contained in the current second + * @property-read int $microsecondsInWeek The number of microseconds contained in the current week + * @property-read int $microsecondsInYear The number of microseconds contained in the current year + * @property-read int $millisecondsInCentury The number of milliseconds contained in the current century + * @property-read int $millisecondsInDay The number of milliseconds contained in the current day + * @property-read int $millisecondsInDecade The number of milliseconds contained in the current decade + * @property-read int $millisecondsInHour The number of milliseconds contained in the current hour + * @property-read int $millisecondsInMillennium The number of milliseconds contained in the current millennium + * @property-read int $millisecondsInMinute The number of milliseconds contained in the current minute + * @property-read int $millisecondsInMonth The number of milliseconds contained in the current month + * @property-read int $millisecondsInQuarter The number of milliseconds contained in the current quarter + * @property-read int $millisecondsInSecond The number of milliseconds contained in the current second + * @property-read int $millisecondsInWeek The number of milliseconds contained in the current week + * @property-read int $millisecondsInYear The number of milliseconds contained in the current year + * @property-read int $minutesInCentury The number of minutes contained in the current century + * @property-read int $minutesInDay The number of minutes contained in the current day + * @property-read int $minutesInDecade The number of minutes contained in the current decade + * @property-read int $minutesInHour The number of minutes contained in the current hour + * @property-read int $minutesInMillennium The number of minutes contained in the current millennium + * @property-read int $minutesInMonth The number of minutes contained in the current month + * @property-read int $minutesInQuarter The number of minutes contained in the current quarter + * @property-read int $minutesInWeek The number of minutes contained in the current week + * @property-read int $minutesInYear The number of minutes contained in the current year + * @property-read int $monthsInCentury The number of months contained in the current century + * @property-read int $monthsInDecade The number of months contained in the current decade + * @property-read int $monthsInMillennium The number of months contained in the current millennium + * @property-read int $monthsInQuarter The number of months contained in the current quarter + * @property-read int $monthsInYear The number of months contained in the current year + * @property-read int $quartersInCentury The number of quarters contained in the current century + * @property-read int $quartersInDecade The number of quarters contained in the current decade + * @property-read int $quartersInMillennium The number of quarters contained in the current millennium + * @property-read int $quartersInYear The number of quarters contained in the current year + * @property-read int $secondsInCentury The number of seconds contained in the current century + * @property-read int $secondsInDay The number of seconds contained in the current day + * @property-read int $secondsInDecade The number of seconds contained in the current decade + * @property-read int $secondsInHour The number of seconds contained in the current hour + * @property-read int $secondsInMillennium The number of seconds contained in the current millennium + * @property-read int $secondsInMinute The number of seconds contained in the current minute + * @property-read int $secondsInMonth The number of seconds contained in the current month + * @property-read int $secondsInQuarter The number of seconds contained in the current quarter + * @property-read int $secondsInWeek The number of seconds contained in the current week + * @property-read int $secondsInYear The number of seconds contained in the current year + * @property-read int $weeksInCentury The number of weeks contained in the current century + * @property-read int $weeksInDecade The number of weeks contained in the current decade + * @property-read int $weeksInMillennium The number of weeks contained in the current millennium + * @property-read int $weeksInMonth The number of weeks contained in the current month + * @property-read int $weeksInQuarter The number of weeks contained in the current quarter + * @property-read int $weeksInYear 51 through 53 + * @property-read int $yearsInCentury The number of years contained in the current century + * @property-read int $yearsInDecade The number of years contained in the current decade + * @property-read int $yearsInMillennium The number of years contained in the current millennium * - * @method bool isUtc() Check if the current instance has UTC timezone. (Both isUtc and isUTC cases are valid.) - * @method bool isLocal() Check if the current instance has non-UTC timezone. - * @method bool isValid() Check if the current instance is a valid date. - * @method bool isDST() Check if the current instance is in a daylight saving time. - * @method bool isSunday() Checks if the instance day is sunday. - * @method bool isMonday() Checks if the instance day is monday. - * @method bool isTuesday() Checks if the instance day is tuesday. - * @method bool isWednesday() Checks if the instance day is wednesday. - * @method bool isThursday() Checks if the instance day is thursday. - * @method bool isFriday() Checks if the instance day is friday. - * @method bool isSaturday() Checks if the instance day is saturday. - * @method bool isSameYear(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same year as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentYear() Checks if the instance is in the same year as the current moment. - * @method bool isNextYear() Checks if the instance is in the same year as the current moment next year. - * @method bool isLastYear() Checks if the instance is in the same year as the current moment last year. - * @method bool isSameWeek(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same week as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentWeek() Checks if the instance is in the same week as the current moment. - * @method bool isNextWeek() Checks if the instance is in the same week as the current moment next week. - * @method bool isLastWeek() Checks if the instance is in the same week as the current moment last week. - * @method bool isSameDay(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same day as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentDay() Checks if the instance is in the same day as the current moment. - * @method bool isNextDay() Checks if the instance is in the same day as the current moment next day. - * @method bool isLastDay() Checks if the instance is in the same day as the current moment last day. - * @method bool isSameHour(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same hour as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentHour() Checks if the instance is in the same hour as the current moment. - * @method bool isNextHour() Checks if the instance is in the same hour as the current moment next hour. - * @method bool isLastHour() Checks if the instance is in the same hour as the current moment last hour. - * @method bool isSameMinute(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same minute as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentMinute() Checks if the instance is in the same minute as the current moment. - * @method bool isNextMinute() Checks if the instance is in the same minute as the current moment next minute. - * @method bool isLastMinute() Checks if the instance is in the same minute as the current moment last minute. - * @method bool isSameSecond(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same second as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentSecond() Checks if the instance is in the same second as the current moment. - * @method bool isNextSecond() Checks if the instance is in the same second as the current moment next second. - * @method bool isLastSecond() Checks if the instance is in the same second as the current moment last second. - * @method bool isSameMicro(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentMicro() Checks if the instance is in the same microsecond as the current moment. - * @method bool isNextMicro() Checks if the instance is in the same microsecond as the current moment next microsecond. - * @method bool isLastMicro() Checks if the instance is in the same microsecond as the current moment last microsecond. - * @method bool isSameMicrosecond(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentMicrosecond() Checks if the instance is in the same microsecond as the current moment. - * @method bool isNextMicrosecond() Checks if the instance is in the same microsecond as the current moment next microsecond. - * @method bool isLastMicrosecond() Checks if the instance is in the same microsecond as the current moment last microsecond. - * @method bool isCurrentMonth() Checks if the instance is in the same month as the current moment. - * @method bool isNextMonth() Checks if the instance is in the same month as the current moment next month. - * @method bool isLastMonth() Checks if the instance is in the same month as the current moment last month. - * @method bool isCurrentQuarter() Checks if the instance is in the same quarter as the current moment. - * @method bool isNextQuarter() Checks if the instance is in the same quarter as the current moment next quarter. - * @method bool isLastQuarter() Checks if the instance is in the same quarter as the current moment last quarter. - * @method bool isSameDecade(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same decade as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentDecade() Checks if the instance is in the same decade as the current moment. - * @method bool isNextDecade() Checks if the instance is in the same decade as the current moment next decade. - * @method bool isLastDecade() Checks if the instance is in the same decade as the current moment last decade. - * @method bool isSameCentury(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same century as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentCentury() Checks if the instance is in the same century as the current moment. - * @method bool isNextCentury() Checks if the instance is in the same century as the current moment next century. - * @method bool isLastCentury() Checks if the instance is in the same century as the current moment last century. - * @method bool isSameMillennium(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same millennium as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentMillennium() Checks if the instance is in the same millennium as the current moment. - * @method bool isNextMillennium() Checks if the instance is in the same millennium as the current moment next millennium. - * @method bool isLastMillennium() Checks if the instance is in the same millennium as the current moment last millennium. - * @method $this years(int $value) Set current instance year to the given value. - * @method $this year(int $value) Set current instance year to the given value. - * @method $this setYears(int $value) Set current instance year to the given value. - * @method $this setYear(int $value) Set current instance year to the given value. - * @method $this months(int $value) Set current instance month to the given value. - * @method $this month(int $value) Set current instance month to the given value. - * @method $this setMonths(int $value) Set current instance month to the given value. - * @method $this setMonth(int $value) Set current instance month to the given value. - * @method $this days(int $value) Set current instance day to the given value. - * @method $this day(int $value) Set current instance day to the given value. - * @method $this setDays(int $value) Set current instance day to the given value. - * @method $this setDay(int $value) Set current instance day to the given value. - * @method $this hours(int $value) Set current instance hour to the given value. - * @method $this hour(int $value) Set current instance hour to the given value. - * @method $this setHours(int $value) Set current instance hour to the given value. - * @method $this setHour(int $value) Set current instance hour to the given value. - * @method $this minutes(int $value) Set current instance minute to the given value. - * @method $this minute(int $value) Set current instance minute to the given value. - * @method $this setMinutes(int $value) Set current instance minute to the given value. - * @method $this setMinute(int $value) Set current instance minute to the given value. - * @method $this seconds(int $value) Set current instance second to the given value. - * @method $this second(int $value) Set current instance second to the given value. - * @method $this setSeconds(int $value) Set current instance second to the given value. - * @method $this setSecond(int $value) Set current instance second to the given value. - * @method $this millis(int $value) Set current instance millisecond to the given value. - * @method $this milli(int $value) Set current instance millisecond to the given value. - * @method $this setMillis(int $value) Set current instance millisecond to the given value. - * @method $this setMilli(int $value) Set current instance millisecond to the given value. - * @method $this milliseconds(int $value) Set current instance millisecond to the given value. - * @method $this millisecond(int $value) Set current instance millisecond to the given value. - * @method $this setMilliseconds(int $value) Set current instance millisecond to the given value. - * @method $this setMillisecond(int $value) Set current instance millisecond to the given value. - * @method $this micros(int $value) Set current instance microsecond to the given value. - * @method $this micro(int $value) Set current instance microsecond to the given value. - * @method $this setMicros(int $value) Set current instance microsecond to the given value. - * @method $this setMicro(int $value) Set current instance microsecond to the given value. - * @method $this microseconds(int $value) Set current instance microsecond to the given value. - * @method $this microsecond(int $value) Set current instance microsecond to the given value. - * @method $this setMicroseconds(int $value) Set current instance microsecond to the given value. - * @method $this setMicrosecond(int $value) Set current instance microsecond to the given value. - * @method $this addYears(int $value = 1) Add years (the $value count passed in) to the instance (using date interval). - * @method $this addYear() Add one year to the instance (using date interval). - * @method $this subYears(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval). - * @method $this subYear() Sub one year to the instance (using date interval). - * @method $this addYearsWithOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method $this addYearWithOverflow() Add one year to the instance (using date interval) with overflow explicitly allowed. - * @method $this subYearsWithOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method $this subYearWithOverflow() Sub one year to the instance (using date interval) with overflow explicitly allowed. - * @method $this addYearsWithoutOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addYearWithoutOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subYearsWithoutOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subYearWithoutOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addYearsWithNoOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addYearWithNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subYearsWithNoOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subYearWithNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addYearsNoOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addYearNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subYearsNoOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subYearNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addMonths(int $value = 1) Add months (the $value count passed in) to the instance (using date interval). - * @method $this addMonth() Add one month to the instance (using date interval). - * @method $this subMonths(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval). - * @method $this subMonth() Sub one month to the instance (using date interval). - * @method $this addMonthsWithOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method $this addMonthWithOverflow() Add one month to the instance (using date interval) with overflow explicitly allowed. - * @method $this subMonthsWithOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method $this subMonthWithOverflow() Sub one month to the instance (using date interval) with overflow explicitly allowed. - * @method $this addMonthsWithoutOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addMonthWithoutOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subMonthsWithoutOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subMonthWithoutOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addMonthsWithNoOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addMonthWithNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subMonthsWithNoOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subMonthWithNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addMonthsNoOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addMonthNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subMonthsNoOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subMonthNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addDays(int $value = 1) Add days (the $value count passed in) to the instance (using date interval). - * @method $this addDay() Add one day to the instance (using date interval). - * @method $this subDays(int $value = 1) Sub days (the $value count passed in) to the instance (using date interval). - * @method $this subDay() Sub one day to the instance (using date interval). - * @method $this addHours(int $value = 1) Add hours (the $value count passed in) to the instance (using date interval). - * @method $this addHour() Add one hour to the instance (using date interval). - * @method $this subHours(int $value = 1) Sub hours (the $value count passed in) to the instance (using date interval). - * @method $this subHour() Sub one hour to the instance (using date interval). - * @method $this addMinutes(int $value = 1) Add minutes (the $value count passed in) to the instance (using date interval). - * @method $this addMinute() Add one minute to the instance (using date interval). - * @method $this subMinutes(int $value = 1) Sub minutes (the $value count passed in) to the instance (using date interval). - * @method $this subMinute() Sub one minute to the instance (using date interval). - * @method $this addSeconds(int $value = 1) Add seconds (the $value count passed in) to the instance (using date interval). - * @method $this addSecond() Add one second to the instance (using date interval). - * @method $this subSeconds(int $value = 1) Sub seconds (the $value count passed in) to the instance (using date interval). - * @method $this subSecond() Sub one second to the instance (using date interval). - * @method $this addMillis(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). - * @method $this addMilli() Add one millisecond to the instance (using date interval). - * @method $this subMillis(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). - * @method $this subMilli() Sub one millisecond to the instance (using date interval). - * @method $this addMilliseconds(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). - * @method $this addMillisecond() Add one millisecond to the instance (using date interval). - * @method $this subMilliseconds(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). - * @method $this subMillisecond() Sub one millisecond to the instance (using date interval). - * @method $this addMicros(int $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). - * @method $this addMicro() Add one microsecond to the instance (using date interval). - * @method $this subMicros(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). - * @method $this subMicro() Sub one microsecond to the instance (using date interval). - * @method $this addMicroseconds(int $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). - * @method $this addMicrosecond() Add one microsecond to the instance (using date interval). - * @method $this subMicroseconds(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). - * @method $this subMicrosecond() Sub one microsecond to the instance (using date interval). - * @method $this addMillennia(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval). - * @method $this addMillennium() Add one millennium to the instance (using date interval). - * @method $this subMillennia(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval). - * @method $this subMillennium() Sub one millennium to the instance (using date interval). - * @method $this addMillenniaWithOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method $this addMillenniumWithOverflow() Add one millennium to the instance (using date interval) with overflow explicitly allowed. - * @method $this subMillenniaWithOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method $this subMillenniumWithOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly allowed. - * @method $this addMillenniaWithoutOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addMillenniumWithoutOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subMillenniaWithoutOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subMillenniumWithoutOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addMillenniaWithNoOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addMillenniumWithNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subMillenniaWithNoOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subMillenniumWithNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addMillenniaNoOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addMillenniumNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subMillenniaNoOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subMillenniumNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addCenturies(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval). - * @method $this addCentury() Add one century to the instance (using date interval). - * @method $this subCenturies(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval). - * @method $this subCentury() Sub one century to the instance (using date interval). - * @method $this addCenturiesWithOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method $this addCenturyWithOverflow() Add one century to the instance (using date interval) with overflow explicitly allowed. - * @method $this subCenturiesWithOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method $this subCenturyWithOverflow() Sub one century to the instance (using date interval) with overflow explicitly allowed. - * @method $this addCenturiesWithoutOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addCenturyWithoutOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subCenturiesWithoutOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subCenturyWithoutOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addCenturiesWithNoOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addCenturyWithNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subCenturiesWithNoOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subCenturyWithNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addCenturiesNoOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addCenturyNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subCenturiesNoOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subCenturyNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addDecades(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval). - * @method $this addDecade() Add one decade to the instance (using date interval). - * @method $this subDecades(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval). - * @method $this subDecade() Sub one decade to the instance (using date interval). - * @method $this addDecadesWithOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method $this addDecadeWithOverflow() Add one decade to the instance (using date interval) with overflow explicitly allowed. - * @method $this subDecadesWithOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method $this subDecadeWithOverflow() Sub one decade to the instance (using date interval) with overflow explicitly allowed. - * @method $this addDecadesWithoutOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addDecadeWithoutOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subDecadesWithoutOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subDecadeWithoutOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addDecadesWithNoOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addDecadeWithNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subDecadesWithNoOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subDecadeWithNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addDecadesNoOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addDecadeNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subDecadesNoOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subDecadeNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addQuarters(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval). - * @method $this addQuarter() Add one quarter to the instance (using date interval). - * @method $this subQuarters(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval). - * @method $this subQuarter() Sub one quarter to the instance (using date interval). - * @method $this addQuartersWithOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method $this addQuarterWithOverflow() Add one quarter to the instance (using date interval) with overflow explicitly allowed. - * @method $this subQuartersWithOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method $this subQuarterWithOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly allowed. - * @method $this addQuartersWithoutOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addQuarterWithoutOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subQuartersWithoutOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subQuarterWithoutOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addQuartersWithNoOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addQuarterWithNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subQuartersWithNoOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subQuarterWithNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addQuartersNoOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addQuarterNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subQuartersNoOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method $this subQuarterNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method $this addWeeks(int $value = 1) Add weeks (the $value count passed in) to the instance (using date interval). - * @method $this addWeek() Add one week to the instance (using date interval). - * @method $this subWeeks(int $value = 1) Sub weeks (the $value count passed in) to the instance (using date interval). - * @method $this subWeek() Sub one week to the instance (using date interval). - * @method $this addWeekdays(int $value = 1) Add weekdays (the $value count passed in) to the instance (using date interval). - * @method $this addWeekday() Add one weekday to the instance (using date interval). - * @method $this subWeekdays(int $value = 1) Sub weekdays (the $value count passed in) to the instance (using date interval). - * @method $this subWeekday() Sub one weekday to the instance (using date interval). - * @method $this addRealMicros(int $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). - * @method $this addRealMicro() Add one microsecond to the instance (using timestamp). - * @method $this subRealMicros(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). - * @method $this subRealMicro() Sub one microsecond to the instance (using timestamp). - * @method CarbonPeriod microsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. - * @method $this addRealMicroseconds(int $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). - * @method $this addRealMicrosecond() Add one microsecond to the instance (using timestamp). - * @method $this subRealMicroseconds(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). - * @method $this subRealMicrosecond() Sub one microsecond to the instance (using timestamp). - * @method CarbonPeriod microsecondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. - * @method $this addRealMillis(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). - * @method $this addRealMilli() Add one millisecond to the instance (using timestamp). - * @method $this subRealMillis(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). - * @method $this subRealMilli() Sub one millisecond to the instance (using timestamp). - * @method CarbonPeriod millisUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. - * @method $this addRealMilliseconds(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). - * @method $this addRealMillisecond() Add one millisecond to the instance (using timestamp). - * @method $this subRealMilliseconds(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). - * @method $this subRealMillisecond() Sub one millisecond to the instance (using timestamp). - * @method CarbonPeriod millisecondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. - * @method $this addRealSeconds(int $value = 1) Add seconds (the $value count passed in) to the instance (using timestamp). - * @method $this addRealSecond() Add one second to the instance (using timestamp). - * @method $this subRealSeconds(int $value = 1) Sub seconds (the $value count passed in) to the instance (using timestamp). - * @method $this subRealSecond() Sub one second to the instance (using timestamp). - * @method CarbonPeriod secondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each second or every X seconds if a factor is given. - * @method $this addRealMinutes(int $value = 1) Add minutes (the $value count passed in) to the instance (using timestamp). - * @method $this addRealMinute() Add one minute to the instance (using timestamp). - * @method $this subRealMinutes(int $value = 1) Sub minutes (the $value count passed in) to the instance (using timestamp). - * @method $this subRealMinute() Sub one minute to the instance (using timestamp). - * @method CarbonPeriod minutesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each minute or every X minutes if a factor is given. - * @method $this addRealHours(int $value = 1) Add hours (the $value count passed in) to the instance (using timestamp). - * @method $this addRealHour() Add one hour to the instance (using timestamp). - * @method $this subRealHours(int $value = 1) Sub hours (the $value count passed in) to the instance (using timestamp). - * @method $this subRealHour() Sub one hour to the instance (using timestamp). - * @method CarbonPeriod hoursUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each hour or every X hours if a factor is given. - * @method $this addRealDays(int $value = 1) Add days (the $value count passed in) to the instance (using timestamp). - * @method $this addRealDay() Add one day to the instance (using timestamp). - * @method $this subRealDays(int $value = 1) Sub days (the $value count passed in) to the instance (using timestamp). - * @method $this subRealDay() Sub one day to the instance (using timestamp). - * @method CarbonPeriod daysUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each day or every X days if a factor is given. - * @method $this addRealWeeks(int $value = 1) Add weeks (the $value count passed in) to the instance (using timestamp). - * @method $this addRealWeek() Add one week to the instance (using timestamp). - * @method $this subRealWeeks(int $value = 1) Sub weeks (the $value count passed in) to the instance (using timestamp). - * @method $this subRealWeek() Sub one week to the instance (using timestamp). - * @method CarbonPeriod weeksUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each week or every X weeks if a factor is given. - * @method $this addRealMonths(int $value = 1) Add months (the $value count passed in) to the instance (using timestamp). - * @method $this addRealMonth() Add one month to the instance (using timestamp). - * @method $this subRealMonths(int $value = 1) Sub months (the $value count passed in) to the instance (using timestamp). - * @method $this subRealMonth() Sub one month to the instance (using timestamp). - * @method CarbonPeriod monthsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each month or every X months if a factor is given. - * @method $this addRealQuarters(int $value = 1) Add quarters (the $value count passed in) to the instance (using timestamp). - * @method $this addRealQuarter() Add one quarter to the instance (using timestamp). - * @method $this subRealQuarters(int $value = 1) Sub quarters (the $value count passed in) to the instance (using timestamp). - * @method $this subRealQuarter() Sub one quarter to the instance (using timestamp). - * @method CarbonPeriod quartersUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each quarter or every X quarters if a factor is given. - * @method $this addRealYears(int $value = 1) Add years (the $value count passed in) to the instance (using timestamp). - * @method $this addRealYear() Add one year to the instance (using timestamp). - * @method $this subRealYears(int $value = 1) Sub years (the $value count passed in) to the instance (using timestamp). - * @method $this subRealYear() Sub one year to the instance (using timestamp). - * @method CarbonPeriod yearsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each year or every X years if a factor is given. - * @method $this addRealDecades(int $value = 1) Add decades (the $value count passed in) to the instance (using timestamp). - * @method $this addRealDecade() Add one decade to the instance (using timestamp). - * @method $this subRealDecades(int $value = 1) Sub decades (the $value count passed in) to the instance (using timestamp). - * @method $this subRealDecade() Sub one decade to the instance (using timestamp). - * @method CarbonPeriod decadesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each decade or every X decades if a factor is given. - * @method $this addRealCenturies(int $value = 1) Add centuries (the $value count passed in) to the instance (using timestamp). - * @method $this addRealCentury() Add one century to the instance (using timestamp). - * @method $this subRealCenturies(int $value = 1) Sub centuries (the $value count passed in) to the instance (using timestamp). - * @method $this subRealCentury() Sub one century to the instance (using timestamp). - * @method CarbonPeriod centuriesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each century or every X centuries if a factor is given. - * @method $this addRealMillennia(int $value = 1) Add millennia (the $value count passed in) to the instance (using timestamp). - * @method $this addRealMillennium() Add one millennium to the instance (using timestamp). - * @method $this subRealMillennia(int $value = 1) Sub millennia (the $value count passed in) to the instance (using timestamp). - * @method $this subRealMillennium() Sub one millennium to the instance (using timestamp). - * @method CarbonPeriod millenniaUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millennium or every X millennia if a factor is given. - * @method $this roundYear(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. - * @method $this roundYears(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. - * @method $this floorYear(float $precision = 1) Truncate the current instance year with given precision. - * @method $this floorYears(float $precision = 1) Truncate the current instance year with given precision. - * @method $this ceilYear(float $precision = 1) Ceil the current instance year with given precision. - * @method $this ceilYears(float $precision = 1) Ceil the current instance year with given precision. - * @method $this roundMonth(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function. - * @method $this roundMonths(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function. - * @method $this floorMonth(float $precision = 1) Truncate the current instance month with given precision. - * @method $this floorMonths(float $precision = 1) Truncate the current instance month with given precision. - * @method $this ceilMonth(float $precision = 1) Ceil the current instance month with given precision. - * @method $this ceilMonths(float $precision = 1) Ceil the current instance month with given precision. - * @method $this roundDay(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. - * @method $this roundDays(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. - * @method $this floorDay(float $precision = 1) Truncate the current instance day with given precision. - * @method $this floorDays(float $precision = 1) Truncate the current instance day with given precision. - * @method $this ceilDay(float $precision = 1) Ceil the current instance day with given precision. - * @method $this ceilDays(float $precision = 1) Ceil the current instance day with given precision. - * @method $this roundHour(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function. - * @method $this roundHours(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function. - * @method $this floorHour(float $precision = 1) Truncate the current instance hour with given precision. - * @method $this floorHours(float $precision = 1) Truncate the current instance hour with given precision. - * @method $this ceilHour(float $precision = 1) Ceil the current instance hour with given precision. - * @method $this ceilHours(float $precision = 1) Ceil the current instance hour with given precision. - * @method $this roundMinute(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function. - * @method $this roundMinutes(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function. - * @method $this floorMinute(float $precision = 1) Truncate the current instance minute with given precision. - * @method $this floorMinutes(float $precision = 1) Truncate the current instance minute with given precision. - * @method $this ceilMinute(float $precision = 1) Ceil the current instance minute with given precision. - * @method $this ceilMinutes(float $precision = 1) Ceil the current instance minute with given precision. - * @method $this roundSecond(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function. - * @method $this roundSeconds(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function. - * @method $this floorSecond(float $precision = 1) Truncate the current instance second with given precision. - * @method $this floorSeconds(float $precision = 1) Truncate the current instance second with given precision. - * @method $this ceilSecond(float $precision = 1) Ceil the current instance second with given precision. - * @method $this ceilSeconds(float $precision = 1) Ceil the current instance second with given precision. - * @method $this roundMillennium(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function. - * @method $this roundMillennia(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function. - * @method $this floorMillennium(float $precision = 1) Truncate the current instance millennium with given precision. - * @method $this floorMillennia(float $precision = 1) Truncate the current instance millennium with given precision. - * @method $this ceilMillennium(float $precision = 1) Ceil the current instance millennium with given precision. - * @method $this ceilMillennia(float $precision = 1) Ceil the current instance millennium with given precision. - * @method $this roundCentury(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function. - * @method $this roundCenturies(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function. - * @method $this floorCentury(float $precision = 1) Truncate the current instance century with given precision. - * @method $this floorCenturies(float $precision = 1) Truncate the current instance century with given precision. - * @method $this ceilCentury(float $precision = 1) Ceil the current instance century with given precision. - * @method $this ceilCenturies(float $precision = 1) Ceil the current instance century with given precision. - * @method $this roundDecade(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function. - * @method $this roundDecades(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function. - * @method $this floorDecade(float $precision = 1) Truncate the current instance decade with given precision. - * @method $this floorDecades(float $precision = 1) Truncate the current instance decade with given precision. - * @method $this ceilDecade(float $precision = 1) Ceil the current instance decade with given precision. - * @method $this ceilDecades(float $precision = 1) Ceil the current instance decade with given precision. - * @method $this roundQuarter(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function. - * @method $this roundQuarters(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function. - * @method $this floorQuarter(float $precision = 1) Truncate the current instance quarter with given precision. - * @method $this floorQuarters(float $precision = 1) Truncate the current instance quarter with given precision. - * @method $this ceilQuarter(float $precision = 1) Ceil the current instance quarter with given precision. - * @method $this ceilQuarters(float $precision = 1) Ceil the current instance quarter with given precision. - * @method $this roundMillisecond(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function. - * @method $this roundMilliseconds(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function. - * @method $this floorMillisecond(float $precision = 1) Truncate the current instance millisecond with given precision. - * @method $this floorMilliseconds(float $precision = 1) Truncate the current instance millisecond with given precision. - * @method $this ceilMillisecond(float $precision = 1) Ceil the current instance millisecond with given precision. - * @method $this ceilMilliseconds(float $precision = 1) Ceil the current instance millisecond with given precision. - * @method $this roundMicrosecond(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function. - * @method $this roundMicroseconds(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function. - * @method $this floorMicrosecond(float $precision = 1) Truncate the current instance microsecond with given precision. - * @method $this floorMicroseconds(float $precision = 1) Truncate the current instance microsecond with given precision. - * @method $this ceilMicrosecond(float $precision = 1) Ceil the current instance microsecond with given precision. - * @method $this ceilMicroseconds(float $precision = 1) Ceil the current instance microsecond with given precision. - * @method string shortAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) - * @method string longAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) - * @method string shortRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) - * @method string longRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) - * @method string shortRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) - * @method string longRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) - * @method string shortRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) - * @method string longRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) - * @method static static|false createFromFormat(string $format, string $time, DateTimeZone|string|false|null $timezone = null) Parse a string into a new Carbon object according to the specified format. - * @method static static __set_state(array $array) https://php.net/manual/en/datetime.set-state.php + * @method bool isUtc() Check if the current instance has UTC timezone. (Both isUtc and isUTC cases are valid.) + * @method bool isLocal() Check if the current instance has non-UTC timezone. + * @method bool isValid() Check if the current instance is a valid date. + * @method bool isDST() Check if the current instance is in a daylight saving time. + * @method bool isSunday() Checks if the instance day is sunday. + * @method bool isMonday() Checks if the instance day is monday. + * @method bool isTuesday() Checks if the instance day is tuesday. + * @method bool isWednesday() Checks if the instance day is wednesday. + * @method bool isThursday() Checks if the instance day is thursday. + * @method bool isFriday() Checks if the instance day is friday. + * @method bool isSaturday() Checks if the instance day is saturday. + * @method bool isSameYear(DateTimeInterface|string $date) Checks if the given date is in the same year as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentYear() Checks if the instance is in the same year as the current moment. + * @method bool isNextYear() Checks if the instance is in the same year as the current moment next year. + * @method bool isLastYear() Checks if the instance is in the same year as the current moment last year. + * @method bool isCurrentMonth() Checks if the instance is in the same month as the current moment. + * @method bool isNextMonth() Checks if the instance is in the same month as the current moment next month. + * @method bool isLastMonth() Checks if the instance is in the same month as the current moment last month. + * @method bool isSameWeek(DateTimeInterface|string $date) Checks if the given date is in the same week as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentWeek() Checks if the instance is in the same week as the current moment. + * @method bool isNextWeek() Checks if the instance is in the same week as the current moment next week. + * @method bool isLastWeek() Checks if the instance is in the same week as the current moment last week. + * @method bool isSameDay(DateTimeInterface|string $date) Checks if the given date is in the same day as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentDay() Checks if the instance is in the same day as the current moment. + * @method bool isNextDay() Checks if the instance is in the same day as the current moment next day. + * @method bool isLastDay() Checks if the instance is in the same day as the current moment last day. + * @method bool isSameHour(DateTimeInterface|string $date) Checks if the given date is in the same hour as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentHour() Checks if the instance is in the same hour as the current moment. + * @method bool isNextHour() Checks if the instance is in the same hour as the current moment next hour. + * @method bool isLastHour() Checks if the instance is in the same hour as the current moment last hour. + * @method bool isSameMinute(DateTimeInterface|string $date) Checks if the given date is in the same minute as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMinute() Checks if the instance is in the same minute as the current moment. + * @method bool isNextMinute() Checks if the instance is in the same minute as the current moment next minute. + * @method bool isLastMinute() Checks if the instance is in the same minute as the current moment last minute. + * @method bool isSameSecond(DateTimeInterface|string $date) Checks if the given date is in the same second as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentSecond() Checks if the instance is in the same second as the current moment. + * @method bool isNextSecond() Checks if the instance is in the same second as the current moment next second. + * @method bool isLastSecond() Checks if the instance is in the same second as the current moment last second. + * @method bool isSameMilli(DateTimeInterface|string $date) Checks if the given date is in the same millisecond as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMilli() Checks if the instance is in the same millisecond as the current moment. + * @method bool isNextMilli() Checks if the instance is in the same millisecond as the current moment next millisecond. + * @method bool isLastMilli() Checks if the instance is in the same millisecond as the current moment last millisecond. + * @method bool isSameMillisecond(DateTimeInterface|string $date) Checks if the given date is in the same millisecond as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMillisecond() Checks if the instance is in the same millisecond as the current moment. + * @method bool isNextMillisecond() Checks if the instance is in the same millisecond as the current moment next millisecond. + * @method bool isLastMillisecond() Checks if the instance is in the same millisecond as the current moment last millisecond. + * @method bool isSameMicro(DateTimeInterface|string $date) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMicro() Checks if the instance is in the same microsecond as the current moment. + * @method bool isNextMicro() Checks if the instance is in the same microsecond as the current moment next microsecond. + * @method bool isLastMicro() Checks if the instance is in the same microsecond as the current moment last microsecond. + * @method bool isSameMicrosecond(DateTimeInterface|string $date) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMicrosecond() Checks if the instance is in the same microsecond as the current moment. + * @method bool isNextMicrosecond() Checks if the instance is in the same microsecond as the current moment next microsecond. + * @method bool isLastMicrosecond() Checks if the instance is in the same microsecond as the current moment last microsecond. + * @method bool isSameDecade(DateTimeInterface|string $date) Checks if the given date is in the same decade as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentDecade() Checks if the instance is in the same decade as the current moment. + * @method bool isNextDecade() Checks if the instance is in the same decade as the current moment next decade. + * @method bool isLastDecade() Checks if the instance is in the same decade as the current moment last decade. + * @method bool isSameCentury(DateTimeInterface|string $date) Checks if the given date is in the same century as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentCentury() Checks if the instance is in the same century as the current moment. + * @method bool isNextCentury() Checks if the instance is in the same century as the current moment next century. + * @method bool isLastCentury() Checks if the instance is in the same century as the current moment last century. + * @method bool isSameMillennium(DateTimeInterface|string $date) Checks if the given date is in the same millennium as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMillennium() Checks if the instance is in the same millennium as the current moment. + * @method bool isNextMillennium() Checks if the instance is in the same millennium as the current moment next millennium. + * @method bool isLastMillennium() Checks if the instance is in the same millennium as the current moment last millennium. + * @method bool isCurrentQuarter() Checks if the instance is in the same quarter as the current moment. + * @method bool isNextQuarter() Checks if the instance is in the same quarter as the current moment next quarter. + * @method bool isLastQuarter() Checks if the instance is in the same quarter as the current moment last quarter. + * @method $this years(int $value) Set current instance year to the given value. + * @method $this year(int $value) Set current instance year to the given value. + * @method $this setYears(int $value) Set current instance year to the given value. + * @method $this setYear(int $value) Set current instance year to the given value. + * @method $this months(Month|int $value) Set current instance month to the given value. + * @method $this month(Month|int $value) Set current instance month to the given value. + * @method $this setMonths(Month|int $value) Set current instance month to the given value. + * @method $this setMonth(Month|int $value) Set current instance month to the given value. + * @method $this days(int $value) Set current instance day to the given value. + * @method $this day(int $value) Set current instance day to the given value. + * @method $this setDays(int $value) Set current instance day to the given value. + * @method $this setDay(int $value) Set current instance day to the given value. + * @method $this hours(int $value) Set current instance hour to the given value. + * @method $this hour(int $value) Set current instance hour to the given value. + * @method $this setHours(int $value) Set current instance hour to the given value. + * @method $this setHour(int $value) Set current instance hour to the given value. + * @method $this minutes(int $value) Set current instance minute to the given value. + * @method $this minute(int $value) Set current instance minute to the given value. + * @method $this setMinutes(int $value) Set current instance minute to the given value. + * @method $this setMinute(int $value) Set current instance minute to the given value. + * @method $this seconds(int $value) Set current instance second to the given value. + * @method $this second(int $value) Set current instance second to the given value. + * @method $this setSeconds(int $value) Set current instance second to the given value. + * @method $this setSecond(int $value) Set current instance second to the given value. + * @method $this millis(int $value) Set current instance millisecond to the given value. + * @method $this milli(int $value) Set current instance millisecond to the given value. + * @method $this setMillis(int $value) Set current instance millisecond to the given value. + * @method $this setMilli(int $value) Set current instance millisecond to the given value. + * @method $this milliseconds(int $value) Set current instance millisecond to the given value. + * @method $this millisecond(int $value) Set current instance millisecond to the given value. + * @method $this setMilliseconds(int $value) Set current instance millisecond to the given value. + * @method $this setMillisecond(int $value) Set current instance millisecond to the given value. + * @method $this micros(int $value) Set current instance microsecond to the given value. + * @method $this micro(int $value) Set current instance microsecond to the given value. + * @method $this setMicros(int $value) Set current instance microsecond to the given value. + * @method $this setMicro(int $value) Set current instance microsecond to the given value. + * @method $this microseconds(int $value) Set current instance microsecond to the given value. + * @method $this microsecond(int $value) Set current instance microsecond to the given value. + * @method $this setMicroseconds(int $value) Set current instance microsecond to the given value. + * @method $this setMicrosecond(int $value) Set current instance microsecond to the given value. + * @method $this addYears(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval). + * @method $this addYear() Add one year to the instance (using date interval). + * @method $this subYears(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval). + * @method $this subYear() Sub one year to the instance (using date interval). + * @method $this addYearsWithOverflow(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method $this addYearWithOverflow() Add one year to the instance (using date interval) with overflow explicitly allowed. + * @method $this subYearsWithOverflow(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method $this subYearWithOverflow() Sub one year to the instance (using date interval) with overflow explicitly allowed. + * @method $this addYearsWithoutOverflow(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addYearWithoutOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subYearsWithoutOverflow(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subYearWithoutOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addYearsWithNoOverflow(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addYearWithNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subYearsWithNoOverflow(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subYearWithNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addYearsNoOverflow(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addYearNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subYearsNoOverflow(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subYearNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addMonths(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval). + * @method $this addMonth() Add one month to the instance (using date interval). + * @method $this subMonths(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval). + * @method $this subMonth() Sub one month to the instance (using date interval). + * @method $this addMonthsWithOverflow(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method $this addMonthWithOverflow() Add one month to the instance (using date interval) with overflow explicitly allowed. + * @method $this subMonthsWithOverflow(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method $this subMonthWithOverflow() Sub one month to the instance (using date interval) with overflow explicitly allowed. + * @method $this addMonthsWithoutOverflow(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addMonthWithoutOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subMonthsWithoutOverflow(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subMonthWithoutOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addMonthsWithNoOverflow(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addMonthWithNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subMonthsWithNoOverflow(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subMonthWithNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addMonthsNoOverflow(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addMonthNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subMonthsNoOverflow(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subMonthNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addDays(int|float $value = 1) Add days (the $value count passed in) to the instance (using date interval). + * @method $this addDay() Add one day to the instance (using date interval). + * @method $this subDays(int|float $value = 1) Sub days (the $value count passed in) to the instance (using date interval). + * @method $this subDay() Sub one day to the instance (using date interval). + * @method $this addHours(int|float $value = 1) Add hours (the $value count passed in) to the instance (using date interval). + * @method $this addHour() Add one hour to the instance (using date interval). + * @method $this subHours(int|float $value = 1) Sub hours (the $value count passed in) to the instance (using date interval). + * @method $this subHour() Sub one hour to the instance (using date interval). + * @method $this addMinutes(int|float $value = 1) Add minutes (the $value count passed in) to the instance (using date interval). + * @method $this addMinute() Add one minute to the instance (using date interval). + * @method $this subMinutes(int|float $value = 1) Sub minutes (the $value count passed in) to the instance (using date interval). + * @method $this subMinute() Sub one minute to the instance (using date interval). + * @method $this addSeconds(int|float $value = 1) Add seconds (the $value count passed in) to the instance (using date interval). + * @method $this addSecond() Add one second to the instance (using date interval). + * @method $this subSeconds(int|float $value = 1) Sub seconds (the $value count passed in) to the instance (using date interval). + * @method $this subSecond() Sub one second to the instance (using date interval). + * @method $this addMillis(int|float $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). + * @method $this addMilli() Add one millisecond to the instance (using date interval). + * @method $this subMillis(int|float $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). + * @method $this subMilli() Sub one millisecond to the instance (using date interval). + * @method $this addMilliseconds(int|float $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). + * @method $this addMillisecond() Add one millisecond to the instance (using date interval). + * @method $this subMilliseconds(int|float $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). + * @method $this subMillisecond() Sub one millisecond to the instance (using date interval). + * @method $this addMicros(int|float $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). + * @method $this addMicro() Add one microsecond to the instance (using date interval). + * @method $this subMicros(int|float $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). + * @method $this subMicro() Sub one microsecond to the instance (using date interval). + * @method $this addMicroseconds(int|float $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). + * @method $this addMicrosecond() Add one microsecond to the instance (using date interval). + * @method $this subMicroseconds(int|float $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). + * @method $this subMicrosecond() Sub one microsecond to the instance (using date interval). + * @method $this addMillennia(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval). + * @method $this addMillennium() Add one millennium to the instance (using date interval). + * @method $this subMillennia(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval). + * @method $this subMillennium() Sub one millennium to the instance (using date interval). + * @method $this addMillenniaWithOverflow(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method $this addMillenniumWithOverflow() Add one millennium to the instance (using date interval) with overflow explicitly allowed. + * @method $this subMillenniaWithOverflow(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method $this subMillenniumWithOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly allowed. + * @method $this addMillenniaWithoutOverflow(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addMillenniumWithoutOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subMillenniaWithoutOverflow(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subMillenniumWithoutOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addMillenniaWithNoOverflow(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addMillenniumWithNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subMillenniaWithNoOverflow(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subMillenniumWithNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addMillenniaNoOverflow(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addMillenniumNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subMillenniaNoOverflow(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subMillenniumNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addCenturies(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval). + * @method $this addCentury() Add one century to the instance (using date interval). + * @method $this subCenturies(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval). + * @method $this subCentury() Sub one century to the instance (using date interval). + * @method $this addCenturiesWithOverflow(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method $this addCenturyWithOverflow() Add one century to the instance (using date interval) with overflow explicitly allowed. + * @method $this subCenturiesWithOverflow(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method $this subCenturyWithOverflow() Sub one century to the instance (using date interval) with overflow explicitly allowed. + * @method $this addCenturiesWithoutOverflow(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addCenturyWithoutOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subCenturiesWithoutOverflow(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subCenturyWithoutOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addCenturiesWithNoOverflow(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addCenturyWithNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subCenturiesWithNoOverflow(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subCenturyWithNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addCenturiesNoOverflow(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addCenturyNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subCenturiesNoOverflow(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subCenturyNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addDecades(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval). + * @method $this addDecade() Add one decade to the instance (using date interval). + * @method $this subDecades(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval). + * @method $this subDecade() Sub one decade to the instance (using date interval). + * @method $this addDecadesWithOverflow(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method $this addDecadeWithOverflow() Add one decade to the instance (using date interval) with overflow explicitly allowed. + * @method $this subDecadesWithOverflow(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method $this subDecadeWithOverflow() Sub one decade to the instance (using date interval) with overflow explicitly allowed. + * @method $this addDecadesWithoutOverflow(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addDecadeWithoutOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subDecadesWithoutOverflow(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subDecadeWithoutOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addDecadesWithNoOverflow(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addDecadeWithNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subDecadesWithNoOverflow(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subDecadeWithNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addDecadesNoOverflow(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addDecadeNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subDecadesNoOverflow(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subDecadeNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addQuarters(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval). + * @method $this addQuarter() Add one quarter to the instance (using date interval). + * @method $this subQuarters(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval). + * @method $this subQuarter() Sub one quarter to the instance (using date interval). + * @method $this addQuartersWithOverflow(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method $this addQuarterWithOverflow() Add one quarter to the instance (using date interval) with overflow explicitly allowed. + * @method $this subQuartersWithOverflow(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method $this subQuarterWithOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly allowed. + * @method $this addQuartersWithoutOverflow(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addQuarterWithoutOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subQuartersWithoutOverflow(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subQuarterWithoutOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addQuartersWithNoOverflow(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addQuarterWithNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subQuartersWithNoOverflow(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subQuarterWithNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addQuartersNoOverflow(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addQuarterNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subQuartersNoOverflow(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subQuarterNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addWeeks(int|float $value = 1) Add weeks (the $value count passed in) to the instance (using date interval). + * @method $this addWeek() Add one week to the instance (using date interval). + * @method $this subWeeks(int|float $value = 1) Sub weeks (the $value count passed in) to the instance (using date interval). + * @method $this subWeek() Sub one week to the instance (using date interval). + * @method $this addWeekdays(int|float $value = 1) Add weekdays (the $value count passed in) to the instance (using date interval). + * @method $this addWeekday() Add one weekday to the instance (using date interval). + * @method $this subWeekdays(int|float $value = 1) Sub weekdays (the $value count passed in) to the instance (using date interval). + * @method $this subWeekday() Sub one weekday to the instance (using date interval). + * @method $this addUTCMicros(int|float $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). + * @method $this addUTCMicro() Add one microsecond to the instance (using timestamp). + * @method $this subUTCMicros(int|float $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). + * @method $this subUTCMicro() Sub one microsecond to the instance (using timestamp). + * @method CarbonPeriod microsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. + * @method float diffInUTCMicros(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of microseconds. + * @method $this addUTCMicroseconds(int|float $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). + * @method $this addUTCMicrosecond() Add one microsecond to the instance (using timestamp). + * @method $this subUTCMicroseconds(int|float $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). + * @method $this subUTCMicrosecond() Sub one microsecond to the instance (using timestamp). + * @method CarbonPeriod microsecondsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. + * @method float diffInUTCMicroseconds(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of microseconds. + * @method $this addUTCMillis(int|float $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). + * @method $this addUTCMilli() Add one millisecond to the instance (using timestamp). + * @method $this subUTCMillis(int|float $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). + * @method $this subUTCMilli() Sub one millisecond to the instance (using timestamp). + * @method CarbonPeriod millisUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. + * @method float diffInUTCMillis(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of milliseconds. + * @method $this addUTCMilliseconds(int|float $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). + * @method $this addUTCMillisecond() Add one millisecond to the instance (using timestamp). + * @method $this subUTCMilliseconds(int|float $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). + * @method $this subUTCMillisecond() Sub one millisecond to the instance (using timestamp). + * @method CarbonPeriod millisecondsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. + * @method float diffInUTCMilliseconds(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of milliseconds. + * @method $this addUTCSeconds(int|float $value = 1) Add seconds (the $value count passed in) to the instance (using timestamp). + * @method $this addUTCSecond() Add one second to the instance (using timestamp). + * @method $this subUTCSeconds(int|float $value = 1) Sub seconds (the $value count passed in) to the instance (using timestamp). + * @method $this subUTCSecond() Sub one second to the instance (using timestamp). + * @method CarbonPeriod secondsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each second or every X seconds if a factor is given. + * @method float diffInUTCSeconds(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of seconds. + * @method $this addUTCMinutes(int|float $value = 1) Add minutes (the $value count passed in) to the instance (using timestamp). + * @method $this addUTCMinute() Add one minute to the instance (using timestamp). + * @method $this subUTCMinutes(int|float $value = 1) Sub minutes (the $value count passed in) to the instance (using timestamp). + * @method $this subUTCMinute() Sub one minute to the instance (using timestamp). + * @method CarbonPeriod minutesUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each minute or every X minutes if a factor is given. + * @method float diffInUTCMinutes(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of minutes. + * @method $this addUTCHours(int|float $value = 1) Add hours (the $value count passed in) to the instance (using timestamp). + * @method $this addUTCHour() Add one hour to the instance (using timestamp). + * @method $this subUTCHours(int|float $value = 1) Sub hours (the $value count passed in) to the instance (using timestamp). + * @method $this subUTCHour() Sub one hour to the instance (using timestamp). + * @method CarbonPeriod hoursUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each hour or every X hours if a factor is given. + * @method float diffInUTCHours(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of hours. + * @method $this addUTCDays(int|float $value = 1) Add days (the $value count passed in) to the instance (using timestamp). + * @method $this addUTCDay() Add one day to the instance (using timestamp). + * @method $this subUTCDays(int|float $value = 1) Sub days (the $value count passed in) to the instance (using timestamp). + * @method $this subUTCDay() Sub one day to the instance (using timestamp). + * @method CarbonPeriod daysUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each day or every X days if a factor is given. + * @method float diffInUTCDays(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of days. + * @method $this addUTCWeeks(int|float $value = 1) Add weeks (the $value count passed in) to the instance (using timestamp). + * @method $this addUTCWeek() Add one week to the instance (using timestamp). + * @method $this subUTCWeeks(int|float $value = 1) Sub weeks (the $value count passed in) to the instance (using timestamp). + * @method $this subUTCWeek() Sub one week to the instance (using timestamp). + * @method CarbonPeriod weeksUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each week or every X weeks if a factor is given. + * @method float diffInUTCWeeks(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of weeks. + * @method $this addUTCMonths(int|float $value = 1) Add months (the $value count passed in) to the instance (using timestamp). + * @method $this addUTCMonth() Add one month to the instance (using timestamp). + * @method $this subUTCMonths(int|float $value = 1) Sub months (the $value count passed in) to the instance (using timestamp). + * @method $this subUTCMonth() Sub one month to the instance (using timestamp). + * @method CarbonPeriod monthsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each month or every X months if a factor is given. + * @method float diffInUTCMonths(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of months. + * @method $this addUTCQuarters(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using timestamp). + * @method $this addUTCQuarter() Add one quarter to the instance (using timestamp). + * @method $this subUTCQuarters(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using timestamp). + * @method $this subUTCQuarter() Sub one quarter to the instance (using timestamp). + * @method CarbonPeriod quartersUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each quarter or every X quarters if a factor is given. + * @method float diffInUTCQuarters(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of quarters. + * @method $this addUTCYears(int|float $value = 1) Add years (the $value count passed in) to the instance (using timestamp). + * @method $this addUTCYear() Add one year to the instance (using timestamp). + * @method $this subUTCYears(int|float $value = 1) Sub years (the $value count passed in) to the instance (using timestamp). + * @method $this subUTCYear() Sub one year to the instance (using timestamp). + * @method CarbonPeriod yearsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each year or every X years if a factor is given. + * @method float diffInUTCYears(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of years. + * @method $this addUTCDecades(int|float $value = 1) Add decades (the $value count passed in) to the instance (using timestamp). + * @method $this addUTCDecade() Add one decade to the instance (using timestamp). + * @method $this subUTCDecades(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using timestamp). + * @method $this subUTCDecade() Sub one decade to the instance (using timestamp). + * @method CarbonPeriod decadesUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each decade or every X decades if a factor is given. + * @method float diffInUTCDecades(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of decades. + * @method $this addUTCCenturies(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using timestamp). + * @method $this addUTCCentury() Add one century to the instance (using timestamp). + * @method $this subUTCCenturies(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using timestamp). + * @method $this subUTCCentury() Sub one century to the instance (using timestamp). + * @method CarbonPeriod centuriesUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each century or every X centuries if a factor is given. + * @method float diffInUTCCenturies(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of centuries. + * @method $this addUTCMillennia(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using timestamp). + * @method $this addUTCMillennium() Add one millennium to the instance (using timestamp). + * @method $this subUTCMillennia(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using timestamp). + * @method $this subUTCMillennium() Sub one millennium to the instance (using timestamp). + * @method CarbonPeriod millenniaUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millennium or every X millennia if a factor is given. + * @method float diffInUTCMillennia(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of millennia. + * @method $this roundYear(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. + * @method $this roundYears(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. + * @method $this floorYear(float $precision = 1) Truncate the current instance year with given precision. + * @method $this floorYears(float $precision = 1) Truncate the current instance year with given precision. + * @method $this ceilYear(float $precision = 1) Ceil the current instance year with given precision. + * @method $this ceilYears(float $precision = 1) Ceil the current instance year with given precision. + * @method $this roundMonth(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function. + * @method $this roundMonths(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function. + * @method $this floorMonth(float $precision = 1) Truncate the current instance month with given precision. + * @method $this floorMonths(float $precision = 1) Truncate the current instance month with given precision. + * @method $this ceilMonth(float $precision = 1) Ceil the current instance month with given precision. + * @method $this ceilMonths(float $precision = 1) Ceil the current instance month with given precision. + * @method $this roundDay(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. + * @method $this roundDays(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. + * @method $this floorDay(float $precision = 1) Truncate the current instance day with given precision. + * @method $this floorDays(float $precision = 1) Truncate the current instance day with given precision. + * @method $this ceilDay(float $precision = 1) Ceil the current instance day with given precision. + * @method $this ceilDays(float $precision = 1) Ceil the current instance day with given precision. + * @method $this roundHour(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function. + * @method $this roundHours(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function. + * @method $this floorHour(float $precision = 1) Truncate the current instance hour with given precision. + * @method $this floorHours(float $precision = 1) Truncate the current instance hour with given precision. + * @method $this ceilHour(float $precision = 1) Ceil the current instance hour with given precision. + * @method $this ceilHours(float $precision = 1) Ceil the current instance hour with given precision. + * @method $this roundMinute(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function. + * @method $this roundMinutes(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function. + * @method $this floorMinute(float $precision = 1) Truncate the current instance minute with given precision. + * @method $this floorMinutes(float $precision = 1) Truncate the current instance minute with given precision. + * @method $this ceilMinute(float $precision = 1) Ceil the current instance minute with given precision. + * @method $this ceilMinutes(float $precision = 1) Ceil the current instance minute with given precision. + * @method $this roundSecond(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function. + * @method $this roundSeconds(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function. + * @method $this floorSecond(float $precision = 1) Truncate the current instance second with given precision. + * @method $this floorSeconds(float $precision = 1) Truncate the current instance second with given precision. + * @method $this ceilSecond(float $precision = 1) Ceil the current instance second with given precision. + * @method $this ceilSeconds(float $precision = 1) Ceil the current instance second with given precision. + * @method $this roundMillennium(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function. + * @method $this roundMillennia(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function. + * @method $this floorMillennium(float $precision = 1) Truncate the current instance millennium with given precision. + * @method $this floorMillennia(float $precision = 1) Truncate the current instance millennium with given precision. + * @method $this ceilMillennium(float $precision = 1) Ceil the current instance millennium with given precision. + * @method $this ceilMillennia(float $precision = 1) Ceil the current instance millennium with given precision. + * @method $this roundCentury(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function. + * @method $this roundCenturies(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function. + * @method $this floorCentury(float $precision = 1) Truncate the current instance century with given precision. + * @method $this floorCenturies(float $precision = 1) Truncate the current instance century with given precision. + * @method $this ceilCentury(float $precision = 1) Ceil the current instance century with given precision. + * @method $this ceilCenturies(float $precision = 1) Ceil the current instance century with given precision. + * @method $this roundDecade(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function. + * @method $this roundDecades(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function. + * @method $this floorDecade(float $precision = 1) Truncate the current instance decade with given precision. + * @method $this floorDecades(float $precision = 1) Truncate the current instance decade with given precision. + * @method $this ceilDecade(float $precision = 1) Ceil the current instance decade with given precision. + * @method $this ceilDecades(float $precision = 1) Ceil the current instance decade with given precision. + * @method $this roundQuarter(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function. + * @method $this roundQuarters(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function. + * @method $this floorQuarter(float $precision = 1) Truncate the current instance quarter with given precision. + * @method $this floorQuarters(float $precision = 1) Truncate the current instance quarter with given precision. + * @method $this ceilQuarter(float $precision = 1) Ceil the current instance quarter with given precision. + * @method $this ceilQuarters(float $precision = 1) Ceil the current instance quarter with given precision. + * @method $this roundMillisecond(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function. + * @method $this roundMilliseconds(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function. + * @method $this floorMillisecond(float $precision = 1) Truncate the current instance millisecond with given precision. + * @method $this floorMilliseconds(float $precision = 1) Truncate the current instance millisecond with given precision. + * @method $this ceilMillisecond(float $precision = 1) Ceil the current instance millisecond with given precision. + * @method $this ceilMilliseconds(float $precision = 1) Ceil the current instance millisecond with given precision. + * @method $this roundMicrosecond(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function. + * @method $this roundMicroseconds(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function. + * @method $this floorMicrosecond(float $precision = 1) Truncate the current instance microsecond with given precision. + * @method $this floorMicroseconds(float $precision = 1) Truncate the current instance microsecond with given precision. + * @method $this ceilMicrosecond(float $precision = 1) Ceil the current instance microsecond with given precision. + * @method $this ceilMicroseconds(float $precision = 1) Ceil the current instance microsecond with given precision. + * @method string shortAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string longAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string shortRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string longRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string shortRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string longRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string shortRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string longRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method int centuriesInMillennium() Return the number of centuries contained in the current millennium + * @method int|static centuryOfMillennium(?int $century = null) Return the value of the century starting from the beginning of the current millennium when called with no parameters, change the current century when called with an integer value + * @method int|static dayOfCentury(?int $day = null) Return the value of the day starting from the beginning of the current century when called with no parameters, change the current day when called with an integer value + * @method int|static dayOfDecade(?int $day = null) Return the value of the day starting from the beginning of the current decade when called with no parameters, change the current day when called with an integer value + * @method int|static dayOfMillennium(?int $day = null) Return the value of the day starting from the beginning of the current millennium when called with no parameters, change the current day when called with an integer value + * @method int|static dayOfMonth(?int $day = null) Return the value of the day starting from the beginning of the current month when called with no parameters, change the current day when called with an integer value + * @method int|static dayOfQuarter(?int $day = null) Return the value of the day starting from the beginning of the current quarter when called with no parameters, change the current day when called with an integer value + * @method int|static dayOfWeek(?int $day = null) Return the value of the day starting from the beginning of the current week when called with no parameters, change the current day when called with an integer value + * @method int daysInCentury() Return the number of days contained in the current century + * @method int daysInDecade() Return the number of days contained in the current decade + * @method int daysInMillennium() Return the number of days contained in the current millennium + * @method int daysInMonth() Return the number of days contained in the current month + * @method int daysInQuarter() Return the number of days contained in the current quarter + * @method int daysInWeek() Return the number of days contained in the current week + * @method int daysInYear() Return the number of days contained in the current year + * @method int|static decadeOfCentury(?int $decade = null) Return the value of the decade starting from the beginning of the current century when called with no parameters, change the current decade when called with an integer value + * @method int|static decadeOfMillennium(?int $decade = null) Return the value of the decade starting from the beginning of the current millennium when called with no parameters, change the current decade when called with an integer value + * @method int decadesInCentury() Return the number of decades contained in the current century + * @method int decadesInMillennium() Return the number of decades contained in the current millennium + * @method int|static hourOfCentury(?int $hour = null) Return the value of the hour starting from the beginning of the current century when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfDay(?int $hour = null) Return the value of the hour starting from the beginning of the current day when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfDecade(?int $hour = null) Return the value of the hour starting from the beginning of the current decade when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfMillennium(?int $hour = null) Return the value of the hour starting from the beginning of the current millennium when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfMonth(?int $hour = null) Return the value of the hour starting from the beginning of the current month when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfQuarter(?int $hour = null) Return the value of the hour starting from the beginning of the current quarter when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfWeek(?int $hour = null) Return the value of the hour starting from the beginning of the current week when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfYear(?int $hour = null) Return the value of the hour starting from the beginning of the current year when called with no parameters, change the current hour when called with an integer value + * @method int hoursInCentury() Return the number of hours contained in the current century + * @method int hoursInDay() Return the number of hours contained in the current day + * @method int hoursInDecade() Return the number of hours contained in the current decade + * @method int hoursInMillennium() Return the number of hours contained in the current millennium + * @method int hoursInMonth() Return the number of hours contained in the current month + * @method int hoursInQuarter() Return the number of hours contained in the current quarter + * @method int hoursInWeek() Return the number of hours contained in the current week + * @method int hoursInYear() Return the number of hours contained in the current year + * @method int|static microsecondOfCentury(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current century when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfDay(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current day when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfDecade(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current decade when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfHour(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current hour when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfMillennium(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current millennium when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfMillisecond(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current millisecond when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfMinute(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current minute when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfMonth(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current month when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfQuarter(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current quarter when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfSecond(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current second when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfWeek(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current week when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfYear(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current year when called with no parameters, change the current microsecond when called with an integer value + * @method int microsecondsInCentury() Return the number of microseconds contained in the current century + * @method int microsecondsInDay() Return the number of microseconds contained in the current day + * @method int microsecondsInDecade() Return the number of microseconds contained in the current decade + * @method int microsecondsInHour() Return the number of microseconds contained in the current hour + * @method int microsecondsInMillennium() Return the number of microseconds contained in the current millennium + * @method int microsecondsInMillisecond() Return the number of microseconds contained in the current millisecond + * @method int microsecondsInMinute() Return the number of microseconds contained in the current minute + * @method int microsecondsInMonth() Return the number of microseconds contained in the current month + * @method int microsecondsInQuarter() Return the number of microseconds contained in the current quarter + * @method int microsecondsInSecond() Return the number of microseconds contained in the current second + * @method int microsecondsInWeek() Return the number of microseconds contained in the current week + * @method int microsecondsInYear() Return the number of microseconds contained in the current year + * @method int|static millisecondOfCentury(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current century when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfDay(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current day when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfDecade(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current decade when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfHour(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current hour when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfMillennium(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current millennium when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfMinute(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current minute when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfMonth(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current month when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfQuarter(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current quarter when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfSecond(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current second when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfWeek(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current week when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfYear(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current year when called with no parameters, change the current millisecond when called with an integer value + * @method int millisecondsInCentury() Return the number of milliseconds contained in the current century + * @method int millisecondsInDay() Return the number of milliseconds contained in the current day + * @method int millisecondsInDecade() Return the number of milliseconds contained in the current decade + * @method int millisecondsInHour() Return the number of milliseconds contained in the current hour + * @method int millisecondsInMillennium() Return the number of milliseconds contained in the current millennium + * @method int millisecondsInMinute() Return the number of milliseconds contained in the current minute + * @method int millisecondsInMonth() Return the number of milliseconds contained in the current month + * @method int millisecondsInQuarter() Return the number of milliseconds contained in the current quarter + * @method int millisecondsInSecond() Return the number of milliseconds contained in the current second + * @method int millisecondsInWeek() Return the number of milliseconds contained in the current week + * @method int millisecondsInYear() Return the number of milliseconds contained in the current year + * @method int|static minuteOfCentury(?int $minute = null) Return the value of the minute starting from the beginning of the current century when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfDay(?int $minute = null) Return the value of the minute starting from the beginning of the current day when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfDecade(?int $minute = null) Return the value of the minute starting from the beginning of the current decade when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfHour(?int $minute = null) Return the value of the minute starting from the beginning of the current hour when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfMillennium(?int $minute = null) Return the value of the minute starting from the beginning of the current millennium when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfMonth(?int $minute = null) Return the value of the minute starting from the beginning of the current month when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfQuarter(?int $minute = null) Return the value of the minute starting from the beginning of the current quarter when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfWeek(?int $minute = null) Return the value of the minute starting from the beginning of the current week when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfYear(?int $minute = null) Return the value of the minute starting from the beginning of the current year when called with no parameters, change the current minute when called with an integer value + * @method int minutesInCentury() Return the number of minutes contained in the current century + * @method int minutesInDay() Return the number of minutes contained in the current day + * @method int minutesInDecade() Return the number of minutes contained in the current decade + * @method int minutesInHour() Return the number of minutes contained in the current hour + * @method int minutesInMillennium() Return the number of minutes contained in the current millennium + * @method int minutesInMonth() Return the number of minutes contained in the current month + * @method int minutesInQuarter() Return the number of minutes contained in the current quarter + * @method int minutesInWeek() Return the number of minutes contained in the current week + * @method int minutesInYear() Return the number of minutes contained in the current year + * @method int|static monthOfCentury(?int $month = null) Return the value of the month starting from the beginning of the current century when called with no parameters, change the current month when called with an integer value + * @method int|static monthOfDecade(?int $month = null) Return the value of the month starting from the beginning of the current decade when called with no parameters, change the current month when called with an integer value + * @method int|static monthOfMillennium(?int $month = null) Return the value of the month starting from the beginning of the current millennium when called with no parameters, change the current month when called with an integer value + * @method int|static monthOfQuarter(?int $month = null) Return the value of the month starting from the beginning of the current quarter when called with no parameters, change the current month when called with an integer value + * @method int|static monthOfYear(?int $month = null) Return the value of the month starting from the beginning of the current year when called with no parameters, change the current month when called with an integer value + * @method int monthsInCentury() Return the number of months contained in the current century + * @method int monthsInDecade() Return the number of months contained in the current decade + * @method int monthsInMillennium() Return the number of months contained in the current millennium + * @method int monthsInQuarter() Return the number of months contained in the current quarter + * @method int monthsInYear() Return the number of months contained in the current year + * @method int|static quarterOfCentury(?int $quarter = null) Return the value of the quarter starting from the beginning of the current century when called with no parameters, change the current quarter when called with an integer value + * @method int|static quarterOfDecade(?int $quarter = null) Return the value of the quarter starting from the beginning of the current decade when called with no parameters, change the current quarter when called with an integer value + * @method int|static quarterOfMillennium(?int $quarter = null) Return the value of the quarter starting from the beginning of the current millennium when called with no parameters, change the current quarter when called with an integer value + * @method int|static quarterOfYear(?int $quarter = null) Return the value of the quarter starting from the beginning of the current year when called with no parameters, change the current quarter when called with an integer value + * @method int quartersInCentury() Return the number of quarters contained in the current century + * @method int quartersInDecade() Return the number of quarters contained in the current decade + * @method int quartersInMillennium() Return the number of quarters contained in the current millennium + * @method int quartersInYear() Return the number of quarters contained in the current year + * @method int|static secondOfCentury(?int $second = null) Return the value of the second starting from the beginning of the current century when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfDay(?int $second = null) Return the value of the second starting from the beginning of the current day when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfDecade(?int $second = null) Return the value of the second starting from the beginning of the current decade when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfHour(?int $second = null) Return the value of the second starting from the beginning of the current hour when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfMillennium(?int $second = null) Return the value of the second starting from the beginning of the current millennium when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfMinute(?int $second = null) Return the value of the second starting from the beginning of the current minute when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfMonth(?int $second = null) Return the value of the second starting from the beginning of the current month when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfQuarter(?int $second = null) Return the value of the second starting from the beginning of the current quarter when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfWeek(?int $second = null) Return the value of the second starting from the beginning of the current week when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfYear(?int $second = null) Return the value of the second starting from the beginning of the current year when called with no parameters, change the current second when called with an integer value + * @method int secondsInCentury() Return the number of seconds contained in the current century + * @method int secondsInDay() Return the number of seconds contained in the current day + * @method int secondsInDecade() Return the number of seconds contained in the current decade + * @method int secondsInHour() Return the number of seconds contained in the current hour + * @method int secondsInMillennium() Return the number of seconds contained in the current millennium + * @method int secondsInMinute() Return the number of seconds contained in the current minute + * @method int secondsInMonth() Return the number of seconds contained in the current month + * @method int secondsInQuarter() Return the number of seconds contained in the current quarter + * @method int secondsInWeek() Return the number of seconds contained in the current week + * @method int secondsInYear() Return the number of seconds contained in the current year + * @method int|static weekOfCentury(?int $week = null) Return the value of the week starting from the beginning of the current century when called with no parameters, change the current week when called with an integer value + * @method int|static weekOfDecade(?int $week = null) Return the value of the week starting from the beginning of the current decade when called with no parameters, change the current week when called with an integer value + * @method int|static weekOfMillennium(?int $week = null) Return the value of the week starting from the beginning of the current millennium when called with no parameters, change the current week when called with an integer value + * @method int|static weekOfMonth(?int $week = null) Return the value of the week starting from the beginning of the current month when called with no parameters, change the current week when called with an integer value + * @method int|static weekOfQuarter(?int $week = null) Return the value of the week starting from the beginning of the current quarter when called with no parameters, change the current week when called with an integer value + * @method int|static weekOfYear(?int $week = null) Return the value of the week starting from the beginning of the current year when called with no parameters, change the current week when called with an integer value + * @method int weeksInCentury() Return the number of weeks contained in the current century + * @method int weeksInDecade() Return the number of weeks contained in the current decade + * @method int weeksInMillennium() Return the number of weeks contained in the current millennium + * @method int weeksInMonth() Return the number of weeks contained in the current month + * @method int weeksInQuarter() Return the number of weeks contained in the current quarter + * @method int|static yearOfCentury(?int $year = null) Return the value of the year starting from the beginning of the current century when called with no parameters, change the current year when called with an integer value + * @method int|static yearOfDecade(?int $year = null) Return the value of the year starting from the beginning of the current decade when called with no parameters, change the current year when called with an integer value + * @method int|static yearOfMillennium(?int $year = null) Return the value of the year starting from the beginning of the current millennium when called with no parameters, change the current year when called with an integer value + * @method int yearsInCentury() Return the number of years contained in the current century + * @method int yearsInDecade() Return the number of years contained in the current decade + * @method int yearsInMillennium() Return the number of years contained in the current millennium * * */ @@ -513,10 +839,8 @@ class Carbon extends DateTime implements CarbonInterface /** * Returns true if the current class/instance is mutable. - * - * @return bool */ - public static function isMutable() + public static function isMutable(): bool { return true; } diff --git a/vendor/nesbot/carbon/src/Carbon/CarbonConverterInterface.php b/vendor/nesbot/carbon/src/Carbon/CarbonConverterInterface.php index 1ce967b25..fd89bd776 100644 --- a/vendor/nesbot/carbon/src/Carbon/CarbonConverterInterface.php +++ b/vendor/nesbot/carbon/src/Carbon/CarbonConverterInterface.php @@ -1,5 +1,7 @@ * - * @property int $year - * @property int $yearIso - * @property int $month - * @property int $day - * @property int $hour - * @property int $minute - * @property int $second - * @property int $micro - * @property int $microsecond - * @property int|float|string $timestamp seconds since the Unix Epoch - * @property string $englishDayOfWeek the day of week in English - * @property string $shortEnglishDayOfWeek the abbreviated day of week in English - * @property string $englishMonth the month in English - * @property string $shortEnglishMonth the abbreviated month in English - * @property int $milliseconds - * @property int $millisecond - * @property int $milli - * @property int $week 1 through 53 - * @property int $isoWeek 1 through 53 - * @property int $weekYear year according to week format - * @property int $isoWeekYear year according to ISO week format - * @property int $dayOfYear 1 through 366 - * @property int $age does a diffInYears() with default parameters - * @property int $offset the timezone offset in seconds from UTC - * @property int $offsetMinutes the timezone offset in minutes from UTC - * @property int $offsetHours the timezone offset in hours from UTC - * @property CarbonTimeZone $timezone the current timezone - * @property CarbonTimeZone $tz alias of $timezone - * @property-read int $dayOfWeek 0 (for Sunday) through 6 (for Saturday) - * @property-read int $dayOfWeekIso 1 (for Monday) through 7 (for Sunday) - * @property-read int $weekOfYear ISO-8601 week number of year, weeks starting on Monday - * @property-read int $daysInMonth number of days in the given month - * @property-read string $latinMeridiem "am"/"pm" (Ante meridiem or Post meridiem latin lowercase mark) - * @property-read string $latinUpperMeridiem "AM"/"PM" (Ante meridiem or Post meridiem latin uppercase mark) - * @property-read string $timezoneAbbreviatedName the current timezone abbreviated name - * @property-read string $tzAbbrName alias of $timezoneAbbreviatedName - * @property-read string $dayName long name of weekday translated according to Carbon locale, in english if no translation available for current language - * @property-read string $shortDayName short name of weekday translated according to Carbon locale, in english if no translation available for current language - * @property-read string $minDayName very short name of weekday translated according to Carbon locale, in english if no translation available for current language - * @property-read string $monthName long name of month translated according to Carbon locale, in english if no translation available for current language - * @property-read string $shortMonthName short name of month translated according to Carbon locale, in english if no translation available for current language - * @property-read string $meridiem lowercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language - * @property-read string $upperMeridiem uppercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language - * @property-read int $noZeroHour current hour from 1 to 24 - * @property-read int $weeksInYear 51 through 53 - * @property-read int $isoWeeksInYear 51 through 53 - * @property-read int $weekOfMonth 1 through 5 - * @property-read int $weekNumberInMonth 1 through 5 - * @property-read int $firstWeekDay 0 through 6 - * @property-read int $lastWeekDay 0 through 6 - * @property-read int $daysInYear 365 or 366 - * @property-read int $quarter the quarter of this instance, 1 - 4 - * @property-read int $decade the decade of this instance - * @property-read int $century the century of this instance - * @property-read int $millennium the millennium of this instance - * @property-read bool $dst daylight savings time indicator, true if DST, false otherwise - * @property-read bool $local checks if the timezone is local, true if local, false otherwise - * @property-read bool $utc checks if the timezone is UTC, true if UTC, false otherwise - * @property-read string $timezoneName the current timezone name - * @property-read string $tzName alias of $timezoneName - * @property-read string $locale locale of the current instance + * @property string $localeDayOfWeek the day of week in current locale + * @property string $shortLocaleDayOfWeek the abbreviated day of week in current locale + * @property string $localeMonth the month in current locale + * @property string $shortLocaleMonth the abbreviated month in current locale + * @property int $year + * @property int $yearIso + * @property int $month + * @property int $day + * @property int $hour + * @property int $minute + * @property int $second + * @property int $micro + * @property int $microsecond + * @property int $dayOfWeekIso 1 (for Monday) through 7 (for Sunday) + * @property int|float|string $timestamp seconds since the Unix Epoch + * @property string $englishDayOfWeek the day of week in English + * @property string $shortEnglishDayOfWeek the abbreviated day of week in English + * @property string $englishMonth the month in English + * @property string $shortEnglishMonth the abbreviated month in English + * @property int $milliseconds + * @property int $millisecond + * @property int $milli + * @property int $week 1 through 53 + * @property int $isoWeek 1 through 53 + * @property int $weekYear year according to week format + * @property int $isoWeekYear year according to ISO week format + * @property int $age does a diffInYears() with default parameters + * @property int $offset the timezone offset in seconds from UTC + * @property int $offsetMinutes the timezone offset in minutes from UTC + * @property int $offsetHours the timezone offset in hours from UTC + * @property CarbonTimeZone $timezone the current timezone + * @property CarbonTimeZone $tz alias of $timezone + * @property int $centuryOfMillennium The value of the century starting from the beginning of the current millennium + * @property int $dayOfCentury The value of the day starting from the beginning of the current century + * @property int $dayOfDecade The value of the day starting from the beginning of the current decade + * @property int $dayOfMillennium The value of the day starting from the beginning of the current millennium + * @property int $dayOfMonth The value of the day starting from the beginning of the current month + * @property int $dayOfQuarter The value of the day starting from the beginning of the current quarter + * @property int $dayOfWeek 0 (for Sunday) through 6 (for Saturday) + * @property int $dayOfYear 1 through 366 + * @property int $decadeOfCentury The value of the decade starting from the beginning of the current century + * @property int $decadeOfMillennium The value of the decade starting from the beginning of the current millennium + * @property int $hourOfCentury The value of the hour starting from the beginning of the current century + * @property int $hourOfDay The value of the hour starting from the beginning of the current day + * @property int $hourOfDecade The value of the hour starting from the beginning of the current decade + * @property int $hourOfMillennium The value of the hour starting from the beginning of the current millennium + * @property int $hourOfMonth The value of the hour starting from the beginning of the current month + * @property int $hourOfQuarter The value of the hour starting from the beginning of the current quarter + * @property int $hourOfWeek The value of the hour starting from the beginning of the current week + * @property int $hourOfYear The value of the hour starting from the beginning of the current year + * @property int $microsecondOfCentury The value of the microsecond starting from the beginning of the current century + * @property int $microsecondOfDay The value of the microsecond starting from the beginning of the current day + * @property int $microsecondOfDecade The value of the microsecond starting from the beginning of the current decade + * @property int $microsecondOfHour The value of the microsecond starting from the beginning of the current hour + * @property int $microsecondOfMillennium The value of the microsecond starting from the beginning of the current millennium + * @property int $microsecondOfMillisecond The value of the microsecond starting from the beginning of the current millisecond + * @property int $microsecondOfMinute The value of the microsecond starting from the beginning of the current minute + * @property int $microsecondOfMonth The value of the microsecond starting from the beginning of the current month + * @property int $microsecondOfQuarter The value of the microsecond starting from the beginning of the current quarter + * @property int $microsecondOfSecond The value of the microsecond starting from the beginning of the current second + * @property int $microsecondOfWeek The value of the microsecond starting from the beginning of the current week + * @property int $microsecondOfYear The value of the microsecond starting from the beginning of the current year + * @property int $millisecondOfCentury The value of the millisecond starting from the beginning of the current century + * @property int $millisecondOfDay The value of the millisecond starting from the beginning of the current day + * @property int $millisecondOfDecade The value of the millisecond starting from the beginning of the current decade + * @property int $millisecondOfHour The value of the millisecond starting from the beginning of the current hour + * @property int $millisecondOfMillennium The value of the millisecond starting from the beginning of the current millennium + * @property int $millisecondOfMinute The value of the millisecond starting from the beginning of the current minute + * @property int $millisecondOfMonth The value of the millisecond starting from the beginning of the current month + * @property int $millisecondOfQuarter The value of the millisecond starting from the beginning of the current quarter + * @property int $millisecondOfSecond The value of the millisecond starting from the beginning of the current second + * @property int $millisecondOfWeek The value of the millisecond starting from the beginning of the current week + * @property int $millisecondOfYear The value of the millisecond starting from the beginning of the current year + * @property int $minuteOfCentury The value of the minute starting from the beginning of the current century + * @property int $minuteOfDay The value of the minute starting from the beginning of the current day + * @property int $minuteOfDecade The value of the minute starting from the beginning of the current decade + * @property int $minuteOfHour The value of the minute starting from the beginning of the current hour + * @property int $minuteOfMillennium The value of the minute starting from the beginning of the current millennium + * @property int $minuteOfMonth The value of the minute starting from the beginning of the current month + * @property int $minuteOfQuarter The value of the minute starting from the beginning of the current quarter + * @property int $minuteOfWeek The value of the minute starting from the beginning of the current week + * @property int $minuteOfYear The value of the minute starting from the beginning of the current year + * @property int $monthOfCentury The value of the month starting from the beginning of the current century + * @property int $monthOfDecade The value of the month starting from the beginning of the current decade + * @property int $monthOfMillennium The value of the month starting from the beginning of the current millennium + * @property int $monthOfQuarter The value of the month starting from the beginning of the current quarter + * @property int $monthOfYear The value of the month starting from the beginning of the current year + * @property int $quarterOfCentury The value of the quarter starting from the beginning of the current century + * @property int $quarterOfDecade The value of the quarter starting from the beginning of the current decade + * @property int $quarterOfMillennium The value of the quarter starting from the beginning of the current millennium + * @property int $quarterOfYear The value of the quarter starting from the beginning of the current year + * @property int $secondOfCentury The value of the second starting from the beginning of the current century + * @property int $secondOfDay The value of the second starting from the beginning of the current day + * @property int $secondOfDecade The value of the second starting from the beginning of the current decade + * @property int $secondOfHour The value of the second starting from the beginning of the current hour + * @property int $secondOfMillennium The value of the second starting from the beginning of the current millennium + * @property int $secondOfMinute The value of the second starting from the beginning of the current minute + * @property int $secondOfMonth The value of the second starting from the beginning of the current month + * @property int $secondOfQuarter The value of the second starting from the beginning of the current quarter + * @property int $secondOfWeek The value of the second starting from the beginning of the current week + * @property int $secondOfYear The value of the second starting from the beginning of the current year + * @property int $weekOfCentury The value of the week starting from the beginning of the current century + * @property int $weekOfDecade The value of the week starting from the beginning of the current decade + * @property int $weekOfMillennium The value of the week starting from the beginning of the current millennium + * @property int $weekOfMonth 1 through 5 + * @property int $weekOfQuarter The value of the week starting from the beginning of the current quarter + * @property int $weekOfYear ISO-8601 week number of year, weeks starting on Monday + * @property int $yearOfCentury The value of the year starting from the beginning of the current century + * @property int $yearOfDecade The value of the year starting from the beginning of the current decade + * @property int $yearOfMillennium The value of the year starting from the beginning of the current millennium + * @property-read string $latinMeridiem "am"/"pm" (Ante meridiem or Post meridiem latin lowercase mark) + * @property-read string $latinUpperMeridiem "AM"/"PM" (Ante meridiem or Post meridiem latin uppercase mark) + * @property-read string $timezoneAbbreviatedName the current timezone abbreviated name + * @property-read string $tzAbbrName alias of $timezoneAbbreviatedName + * @property-read string $dayName long name of weekday translated according to Carbon locale, in english if no translation available for current language + * @property-read string $shortDayName short name of weekday translated according to Carbon locale, in english if no translation available for current language + * @property-read string $minDayName very short name of weekday translated according to Carbon locale, in english if no translation available for current language + * @property-read string $monthName long name of month translated according to Carbon locale, in english if no translation available for current language + * @property-read string $shortMonthName short name of month translated according to Carbon locale, in english if no translation available for current language + * @property-read string $meridiem lowercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language + * @property-read string $upperMeridiem uppercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language + * @property-read int $noZeroHour current hour from 1 to 24 + * @property-read int $isoWeeksInYear 51 through 53 + * @property-read int $weekNumberInMonth 1 through 5 + * @property-read int $firstWeekDay 0 through 6 + * @property-read int $lastWeekDay 0 through 6 + * @property-read int $quarter the quarter of this instance, 1 - 4 + * @property-read int $decade the decade of this instance + * @property-read int $century the century of this instance + * @property-read int $millennium the millennium of this instance + * @property-read bool $dst daylight savings time indicator, true if DST, false otherwise + * @property-read bool $local checks if the timezone is local, true if local, false otherwise + * @property-read bool $utc checks if the timezone is UTC, true if UTC, false otherwise + * @property-read string $timezoneName the current timezone name + * @property-read string $tzName alias of $timezoneName + * @property-read string $locale locale of the current instance + * @property-read int $centuriesInMillennium The number of centuries contained in the current millennium + * @property-read int $daysInCentury The number of days contained in the current century + * @property-read int $daysInDecade The number of days contained in the current decade + * @property-read int $daysInMillennium The number of days contained in the current millennium + * @property-read int $daysInMonth number of days in the given month + * @property-read int $daysInQuarter The number of days contained in the current quarter + * @property-read int $daysInWeek The number of days contained in the current week + * @property-read int $daysInYear 365 or 366 + * @property-read int $decadesInCentury The number of decades contained in the current century + * @property-read int $decadesInMillennium The number of decades contained in the current millennium + * @property-read int $hoursInCentury The number of hours contained in the current century + * @property-read int $hoursInDay The number of hours contained in the current day + * @property-read int $hoursInDecade The number of hours contained in the current decade + * @property-read int $hoursInMillennium The number of hours contained in the current millennium + * @property-read int $hoursInMonth The number of hours contained in the current month + * @property-read int $hoursInQuarter The number of hours contained in the current quarter + * @property-read int $hoursInWeek The number of hours contained in the current week + * @property-read int $hoursInYear The number of hours contained in the current year + * @property-read int $microsecondsInCentury The number of microseconds contained in the current century + * @property-read int $microsecondsInDay The number of microseconds contained in the current day + * @property-read int $microsecondsInDecade The number of microseconds contained in the current decade + * @property-read int $microsecondsInHour The number of microseconds contained in the current hour + * @property-read int $microsecondsInMillennium The number of microseconds contained in the current millennium + * @property-read int $microsecondsInMillisecond The number of microseconds contained in the current millisecond + * @property-read int $microsecondsInMinute The number of microseconds contained in the current minute + * @property-read int $microsecondsInMonth The number of microseconds contained in the current month + * @property-read int $microsecondsInQuarter The number of microseconds contained in the current quarter + * @property-read int $microsecondsInSecond The number of microseconds contained in the current second + * @property-read int $microsecondsInWeek The number of microseconds contained in the current week + * @property-read int $microsecondsInYear The number of microseconds contained in the current year + * @property-read int $millisecondsInCentury The number of milliseconds contained in the current century + * @property-read int $millisecondsInDay The number of milliseconds contained in the current day + * @property-read int $millisecondsInDecade The number of milliseconds contained in the current decade + * @property-read int $millisecondsInHour The number of milliseconds contained in the current hour + * @property-read int $millisecondsInMillennium The number of milliseconds contained in the current millennium + * @property-read int $millisecondsInMinute The number of milliseconds contained in the current minute + * @property-read int $millisecondsInMonth The number of milliseconds contained in the current month + * @property-read int $millisecondsInQuarter The number of milliseconds contained in the current quarter + * @property-read int $millisecondsInSecond The number of milliseconds contained in the current second + * @property-read int $millisecondsInWeek The number of milliseconds contained in the current week + * @property-read int $millisecondsInYear The number of milliseconds contained in the current year + * @property-read int $minutesInCentury The number of minutes contained in the current century + * @property-read int $minutesInDay The number of minutes contained in the current day + * @property-read int $minutesInDecade The number of minutes contained in the current decade + * @property-read int $minutesInHour The number of minutes contained in the current hour + * @property-read int $minutesInMillennium The number of minutes contained in the current millennium + * @property-read int $minutesInMonth The number of minutes contained in the current month + * @property-read int $minutesInQuarter The number of minutes contained in the current quarter + * @property-read int $minutesInWeek The number of minutes contained in the current week + * @property-read int $minutesInYear The number of minutes contained in the current year + * @property-read int $monthsInCentury The number of months contained in the current century + * @property-read int $monthsInDecade The number of months contained in the current decade + * @property-read int $monthsInMillennium The number of months contained in the current millennium + * @property-read int $monthsInQuarter The number of months contained in the current quarter + * @property-read int $monthsInYear The number of months contained in the current year + * @property-read int $quartersInCentury The number of quarters contained in the current century + * @property-read int $quartersInDecade The number of quarters contained in the current decade + * @property-read int $quartersInMillennium The number of quarters contained in the current millennium + * @property-read int $quartersInYear The number of quarters contained in the current year + * @property-read int $secondsInCentury The number of seconds contained in the current century + * @property-read int $secondsInDay The number of seconds contained in the current day + * @property-read int $secondsInDecade The number of seconds contained in the current decade + * @property-read int $secondsInHour The number of seconds contained in the current hour + * @property-read int $secondsInMillennium The number of seconds contained in the current millennium + * @property-read int $secondsInMinute The number of seconds contained in the current minute + * @property-read int $secondsInMonth The number of seconds contained in the current month + * @property-read int $secondsInQuarter The number of seconds contained in the current quarter + * @property-read int $secondsInWeek The number of seconds contained in the current week + * @property-read int $secondsInYear The number of seconds contained in the current year + * @property-read int $weeksInCentury The number of weeks contained in the current century + * @property-read int $weeksInDecade The number of weeks contained in the current decade + * @property-read int $weeksInMillennium The number of weeks contained in the current millennium + * @property-read int $weeksInMonth The number of weeks contained in the current month + * @property-read int $weeksInQuarter The number of weeks contained in the current quarter + * @property-read int $weeksInYear 51 through 53 + * @property-read int $yearsInCentury The number of years contained in the current century + * @property-read int $yearsInDecade The number of years contained in the current decade + * @property-read int $yearsInMillennium The number of years contained in the current millennium * - * @method bool isUtc() Check if the current instance has UTC timezone. (Both isUtc and isUTC cases are valid.) - * @method bool isLocal() Check if the current instance has non-UTC timezone. - * @method bool isValid() Check if the current instance is a valid date. - * @method bool isDST() Check if the current instance is in a daylight saving time. - * @method bool isSunday() Checks if the instance day is sunday. - * @method bool isMonday() Checks if the instance day is monday. - * @method bool isTuesday() Checks if the instance day is tuesday. - * @method bool isWednesday() Checks if the instance day is wednesday. - * @method bool isThursday() Checks if the instance day is thursday. - * @method bool isFriday() Checks if the instance day is friday. - * @method bool isSaturday() Checks if the instance day is saturday. - * @method bool isSameYear(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same year as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentYear() Checks if the instance is in the same year as the current moment. - * @method bool isNextYear() Checks if the instance is in the same year as the current moment next year. - * @method bool isLastYear() Checks if the instance is in the same year as the current moment last year. - * @method bool isSameWeek(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same week as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentWeek() Checks if the instance is in the same week as the current moment. - * @method bool isNextWeek() Checks if the instance is in the same week as the current moment next week. - * @method bool isLastWeek() Checks if the instance is in the same week as the current moment last week. - * @method bool isSameDay(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same day as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentDay() Checks if the instance is in the same day as the current moment. - * @method bool isNextDay() Checks if the instance is in the same day as the current moment next day. - * @method bool isLastDay() Checks if the instance is in the same day as the current moment last day. - * @method bool isSameHour(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same hour as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentHour() Checks if the instance is in the same hour as the current moment. - * @method bool isNextHour() Checks if the instance is in the same hour as the current moment next hour. - * @method bool isLastHour() Checks if the instance is in the same hour as the current moment last hour. - * @method bool isSameMinute(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same minute as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentMinute() Checks if the instance is in the same minute as the current moment. - * @method bool isNextMinute() Checks if the instance is in the same minute as the current moment next minute. - * @method bool isLastMinute() Checks if the instance is in the same minute as the current moment last minute. - * @method bool isSameSecond(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same second as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentSecond() Checks if the instance is in the same second as the current moment. - * @method bool isNextSecond() Checks if the instance is in the same second as the current moment next second. - * @method bool isLastSecond() Checks if the instance is in the same second as the current moment last second. - * @method bool isSameMicro(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentMicro() Checks if the instance is in the same microsecond as the current moment. - * @method bool isNextMicro() Checks if the instance is in the same microsecond as the current moment next microsecond. - * @method bool isLastMicro() Checks if the instance is in the same microsecond as the current moment last microsecond. - * @method bool isSameMicrosecond(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentMicrosecond() Checks if the instance is in the same microsecond as the current moment. - * @method bool isNextMicrosecond() Checks if the instance is in the same microsecond as the current moment next microsecond. - * @method bool isLastMicrosecond() Checks if the instance is in the same microsecond as the current moment last microsecond. - * @method bool isCurrentMonth() Checks if the instance is in the same month as the current moment. - * @method bool isNextMonth() Checks if the instance is in the same month as the current moment next month. - * @method bool isLastMonth() Checks if the instance is in the same month as the current moment last month. - * @method bool isCurrentQuarter() Checks if the instance is in the same quarter as the current moment. - * @method bool isNextQuarter() Checks if the instance is in the same quarter as the current moment next quarter. - * @method bool isLastQuarter() Checks if the instance is in the same quarter as the current moment last quarter. - * @method bool isSameDecade(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same decade as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentDecade() Checks if the instance is in the same decade as the current moment. - * @method bool isNextDecade() Checks if the instance is in the same decade as the current moment next decade. - * @method bool isLastDecade() Checks if the instance is in the same decade as the current moment last decade. - * @method bool isSameCentury(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same century as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentCentury() Checks if the instance is in the same century as the current moment. - * @method bool isNextCentury() Checks if the instance is in the same century as the current moment next century. - * @method bool isLastCentury() Checks if the instance is in the same century as the current moment last century. - * @method bool isSameMillennium(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same millennium as the instance. If null passed, compare to now (with the same timezone). - * @method bool isCurrentMillennium() Checks if the instance is in the same millennium as the current moment. - * @method bool isNextMillennium() Checks if the instance is in the same millennium as the current moment next millennium. - * @method bool isLastMillennium() Checks if the instance is in the same millennium as the current moment last millennium. - * @method CarbonImmutable years(int $value) Set current instance year to the given value. - * @method CarbonImmutable year(int $value) Set current instance year to the given value. - * @method CarbonImmutable setYears(int $value) Set current instance year to the given value. - * @method CarbonImmutable setYear(int $value) Set current instance year to the given value. - * @method CarbonImmutable months(int $value) Set current instance month to the given value. - * @method CarbonImmutable month(int $value) Set current instance month to the given value. - * @method CarbonImmutable setMonths(int $value) Set current instance month to the given value. - * @method CarbonImmutable setMonth(int $value) Set current instance month to the given value. - * @method CarbonImmutable days(int $value) Set current instance day to the given value. - * @method CarbonImmutable day(int $value) Set current instance day to the given value. - * @method CarbonImmutable setDays(int $value) Set current instance day to the given value. - * @method CarbonImmutable setDay(int $value) Set current instance day to the given value. - * @method CarbonImmutable hours(int $value) Set current instance hour to the given value. - * @method CarbonImmutable hour(int $value) Set current instance hour to the given value. - * @method CarbonImmutable setHours(int $value) Set current instance hour to the given value. - * @method CarbonImmutable setHour(int $value) Set current instance hour to the given value. - * @method CarbonImmutable minutes(int $value) Set current instance minute to the given value. - * @method CarbonImmutable minute(int $value) Set current instance minute to the given value. - * @method CarbonImmutable setMinutes(int $value) Set current instance minute to the given value. - * @method CarbonImmutable setMinute(int $value) Set current instance minute to the given value. - * @method CarbonImmutable seconds(int $value) Set current instance second to the given value. - * @method CarbonImmutable second(int $value) Set current instance second to the given value. - * @method CarbonImmutable setSeconds(int $value) Set current instance second to the given value. - * @method CarbonImmutable setSecond(int $value) Set current instance second to the given value. - * @method CarbonImmutable millis(int $value) Set current instance millisecond to the given value. - * @method CarbonImmutable milli(int $value) Set current instance millisecond to the given value. - * @method CarbonImmutable setMillis(int $value) Set current instance millisecond to the given value. - * @method CarbonImmutable setMilli(int $value) Set current instance millisecond to the given value. - * @method CarbonImmutable milliseconds(int $value) Set current instance millisecond to the given value. - * @method CarbonImmutable millisecond(int $value) Set current instance millisecond to the given value. - * @method CarbonImmutable setMilliseconds(int $value) Set current instance millisecond to the given value. - * @method CarbonImmutable setMillisecond(int $value) Set current instance millisecond to the given value. - * @method CarbonImmutable micros(int $value) Set current instance microsecond to the given value. - * @method CarbonImmutable micro(int $value) Set current instance microsecond to the given value. - * @method CarbonImmutable setMicros(int $value) Set current instance microsecond to the given value. - * @method CarbonImmutable setMicro(int $value) Set current instance microsecond to the given value. - * @method CarbonImmutable microseconds(int $value) Set current instance microsecond to the given value. - * @method CarbonImmutable microsecond(int $value) Set current instance microsecond to the given value. - * @method CarbonImmutable setMicroseconds(int $value) Set current instance microsecond to the given value. - * @method CarbonImmutable setMicrosecond(int $value) Set current instance microsecond to the given value. - * @method CarbonImmutable addYears(int $value = 1) Add years (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable addYear() Add one year to the instance (using date interval). - * @method CarbonImmutable subYears(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable subYear() Sub one year to the instance (using date interval). - * @method CarbonImmutable addYearsWithOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonImmutable addYearWithOverflow() Add one year to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonImmutable subYearsWithOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonImmutable subYearWithOverflow() Sub one year to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonImmutable addYearsWithoutOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addYearWithoutOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subYearsWithoutOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subYearWithoutOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addYearsWithNoOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addYearWithNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subYearsWithNoOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subYearWithNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addYearsNoOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addYearNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subYearsNoOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subYearNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addMonths(int $value = 1) Add months (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable addMonth() Add one month to the instance (using date interval). - * @method CarbonImmutable subMonths(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable subMonth() Sub one month to the instance (using date interval). - * @method CarbonImmutable addMonthsWithOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonImmutable addMonthWithOverflow() Add one month to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonImmutable subMonthsWithOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonImmutable subMonthWithOverflow() Sub one month to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonImmutable addMonthsWithoutOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addMonthWithoutOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subMonthsWithoutOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subMonthWithoutOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addMonthsWithNoOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addMonthWithNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subMonthsWithNoOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subMonthWithNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addMonthsNoOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addMonthNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subMonthsNoOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subMonthNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addDays(int $value = 1) Add days (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable addDay() Add one day to the instance (using date interval). - * @method CarbonImmutable subDays(int $value = 1) Sub days (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable subDay() Sub one day to the instance (using date interval). - * @method CarbonImmutable addHours(int $value = 1) Add hours (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable addHour() Add one hour to the instance (using date interval). - * @method CarbonImmutable subHours(int $value = 1) Sub hours (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable subHour() Sub one hour to the instance (using date interval). - * @method CarbonImmutable addMinutes(int $value = 1) Add minutes (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable addMinute() Add one minute to the instance (using date interval). - * @method CarbonImmutable subMinutes(int $value = 1) Sub minutes (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable subMinute() Sub one minute to the instance (using date interval). - * @method CarbonImmutable addSeconds(int $value = 1) Add seconds (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable addSecond() Add one second to the instance (using date interval). - * @method CarbonImmutable subSeconds(int $value = 1) Sub seconds (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable subSecond() Sub one second to the instance (using date interval). - * @method CarbonImmutable addMillis(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable addMilli() Add one millisecond to the instance (using date interval). - * @method CarbonImmutable subMillis(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable subMilli() Sub one millisecond to the instance (using date interval). - * @method CarbonImmutable addMilliseconds(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable addMillisecond() Add one millisecond to the instance (using date interval). - * @method CarbonImmutable subMilliseconds(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable subMillisecond() Sub one millisecond to the instance (using date interval). - * @method CarbonImmutable addMicros(int $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable addMicro() Add one microsecond to the instance (using date interval). - * @method CarbonImmutable subMicros(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable subMicro() Sub one microsecond to the instance (using date interval). - * @method CarbonImmutable addMicroseconds(int $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable addMicrosecond() Add one microsecond to the instance (using date interval). - * @method CarbonImmutable subMicroseconds(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable subMicrosecond() Sub one microsecond to the instance (using date interval). - * @method CarbonImmutable addMillennia(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable addMillennium() Add one millennium to the instance (using date interval). - * @method CarbonImmutable subMillennia(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable subMillennium() Sub one millennium to the instance (using date interval). - * @method CarbonImmutable addMillenniaWithOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonImmutable addMillenniumWithOverflow() Add one millennium to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonImmutable subMillenniaWithOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonImmutable subMillenniumWithOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonImmutable addMillenniaWithoutOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addMillenniumWithoutOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subMillenniaWithoutOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subMillenniumWithoutOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addMillenniaWithNoOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addMillenniumWithNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subMillenniaWithNoOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subMillenniumWithNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addMillenniaNoOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addMillenniumNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subMillenniaNoOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subMillenniumNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addCenturies(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable addCentury() Add one century to the instance (using date interval). - * @method CarbonImmutable subCenturies(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable subCentury() Sub one century to the instance (using date interval). - * @method CarbonImmutable addCenturiesWithOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonImmutable addCenturyWithOverflow() Add one century to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonImmutable subCenturiesWithOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonImmutable subCenturyWithOverflow() Sub one century to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonImmutable addCenturiesWithoutOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addCenturyWithoutOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subCenturiesWithoutOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subCenturyWithoutOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addCenturiesWithNoOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addCenturyWithNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subCenturiesWithNoOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subCenturyWithNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addCenturiesNoOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addCenturyNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subCenturiesNoOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subCenturyNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addDecades(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable addDecade() Add one decade to the instance (using date interval). - * @method CarbonImmutable subDecades(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable subDecade() Sub one decade to the instance (using date interval). - * @method CarbonImmutable addDecadesWithOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonImmutable addDecadeWithOverflow() Add one decade to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonImmutable subDecadesWithOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonImmutable subDecadeWithOverflow() Sub one decade to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonImmutable addDecadesWithoutOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addDecadeWithoutOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subDecadesWithoutOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subDecadeWithoutOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addDecadesWithNoOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addDecadeWithNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subDecadesWithNoOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subDecadeWithNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addDecadesNoOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addDecadeNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subDecadesNoOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subDecadeNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addQuarters(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable addQuarter() Add one quarter to the instance (using date interval). - * @method CarbonImmutable subQuarters(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable subQuarter() Sub one quarter to the instance (using date interval). - * @method CarbonImmutable addQuartersWithOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonImmutable addQuarterWithOverflow() Add one quarter to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonImmutable subQuartersWithOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonImmutable subQuarterWithOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonImmutable addQuartersWithoutOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addQuarterWithoutOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subQuartersWithoutOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subQuarterWithoutOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addQuartersWithNoOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addQuarterWithNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subQuartersWithNoOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subQuarterWithNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addQuartersNoOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addQuarterNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subQuartersNoOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable subQuarterNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonImmutable addWeeks(int $value = 1) Add weeks (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable addWeek() Add one week to the instance (using date interval). - * @method CarbonImmutable subWeeks(int $value = 1) Sub weeks (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable subWeek() Sub one week to the instance (using date interval). - * @method CarbonImmutable addWeekdays(int $value = 1) Add weekdays (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable addWeekday() Add one weekday to the instance (using date interval). - * @method CarbonImmutable subWeekdays(int $value = 1) Sub weekdays (the $value count passed in) to the instance (using date interval). - * @method CarbonImmutable subWeekday() Sub one weekday to the instance (using date interval). - * @method CarbonImmutable addRealMicros(int $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable addRealMicro() Add one microsecond to the instance (using timestamp). - * @method CarbonImmutable subRealMicros(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable subRealMicro() Sub one microsecond to the instance (using timestamp). - * @method CarbonPeriod microsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. - * @method CarbonImmutable addRealMicroseconds(int $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable addRealMicrosecond() Add one microsecond to the instance (using timestamp). - * @method CarbonImmutable subRealMicroseconds(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable subRealMicrosecond() Sub one microsecond to the instance (using timestamp). - * @method CarbonPeriod microsecondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. - * @method CarbonImmutable addRealMillis(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable addRealMilli() Add one millisecond to the instance (using timestamp). - * @method CarbonImmutable subRealMillis(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable subRealMilli() Sub one millisecond to the instance (using timestamp). - * @method CarbonPeriod millisUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. - * @method CarbonImmutable addRealMilliseconds(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable addRealMillisecond() Add one millisecond to the instance (using timestamp). - * @method CarbonImmutable subRealMilliseconds(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable subRealMillisecond() Sub one millisecond to the instance (using timestamp). - * @method CarbonPeriod millisecondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. - * @method CarbonImmutable addRealSeconds(int $value = 1) Add seconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable addRealSecond() Add one second to the instance (using timestamp). - * @method CarbonImmutable subRealSeconds(int $value = 1) Sub seconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable subRealSecond() Sub one second to the instance (using timestamp). - * @method CarbonPeriod secondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each second or every X seconds if a factor is given. - * @method CarbonImmutable addRealMinutes(int $value = 1) Add minutes (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable addRealMinute() Add one minute to the instance (using timestamp). - * @method CarbonImmutable subRealMinutes(int $value = 1) Sub minutes (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable subRealMinute() Sub one minute to the instance (using timestamp). - * @method CarbonPeriod minutesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each minute or every X minutes if a factor is given. - * @method CarbonImmutable addRealHours(int $value = 1) Add hours (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable addRealHour() Add one hour to the instance (using timestamp). - * @method CarbonImmutable subRealHours(int $value = 1) Sub hours (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable subRealHour() Sub one hour to the instance (using timestamp). - * @method CarbonPeriod hoursUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each hour or every X hours if a factor is given. - * @method CarbonImmutable addRealDays(int $value = 1) Add days (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable addRealDay() Add one day to the instance (using timestamp). - * @method CarbonImmutable subRealDays(int $value = 1) Sub days (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable subRealDay() Sub one day to the instance (using timestamp). - * @method CarbonPeriod daysUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each day or every X days if a factor is given. - * @method CarbonImmutable addRealWeeks(int $value = 1) Add weeks (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable addRealWeek() Add one week to the instance (using timestamp). - * @method CarbonImmutable subRealWeeks(int $value = 1) Sub weeks (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable subRealWeek() Sub one week to the instance (using timestamp). - * @method CarbonPeriod weeksUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each week or every X weeks if a factor is given. - * @method CarbonImmutable addRealMonths(int $value = 1) Add months (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable addRealMonth() Add one month to the instance (using timestamp). - * @method CarbonImmutable subRealMonths(int $value = 1) Sub months (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable subRealMonth() Sub one month to the instance (using timestamp). - * @method CarbonPeriod monthsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each month or every X months if a factor is given. - * @method CarbonImmutable addRealQuarters(int $value = 1) Add quarters (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable addRealQuarter() Add one quarter to the instance (using timestamp). - * @method CarbonImmutable subRealQuarters(int $value = 1) Sub quarters (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable subRealQuarter() Sub one quarter to the instance (using timestamp). - * @method CarbonPeriod quartersUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each quarter or every X quarters if a factor is given. - * @method CarbonImmutable addRealYears(int $value = 1) Add years (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable addRealYear() Add one year to the instance (using timestamp). - * @method CarbonImmutable subRealYears(int $value = 1) Sub years (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable subRealYear() Sub one year to the instance (using timestamp). - * @method CarbonPeriod yearsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each year or every X years if a factor is given. - * @method CarbonImmutable addRealDecades(int $value = 1) Add decades (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable addRealDecade() Add one decade to the instance (using timestamp). - * @method CarbonImmutable subRealDecades(int $value = 1) Sub decades (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable subRealDecade() Sub one decade to the instance (using timestamp). - * @method CarbonPeriod decadesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each decade or every X decades if a factor is given. - * @method CarbonImmutable addRealCenturies(int $value = 1) Add centuries (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable addRealCentury() Add one century to the instance (using timestamp). - * @method CarbonImmutable subRealCenturies(int $value = 1) Sub centuries (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable subRealCentury() Sub one century to the instance (using timestamp). - * @method CarbonPeriod centuriesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each century or every X centuries if a factor is given. - * @method CarbonImmutable addRealMillennia(int $value = 1) Add millennia (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable addRealMillennium() Add one millennium to the instance (using timestamp). - * @method CarbonImmutable subRealMillennia(int $value = 1) Sub millennia (the $value count passed in) to the instance (using timestamp). - * @method CarbonImmutable subRealMillennium() Sub one millennium to the instance (using timestamp). - * @method CarbonPeriod millenniaUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millennium or every X millennia if a factor is given. - * @method CarbonImmutable roundYear(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. - * @method CarbonImmutable roundYears(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. - * @method CarbonImmutable floorYear(float $precision = 1) Truncate the current instance year with given precision. - * @method CarbonImmutable floorYears(float $precision = 1) Truncate the current instance year with given precision. - * @method CarbonImmutable ceilYear(float $precision = 1) Ceil the current instance year with given precision. - * @method CarbonImmutable ceilYears(float $precision = 1) Ceil the current instance year with given precision. - * @method CarbonImmutable roundMonth(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function. - * @method CarbonImmutable roundMonths(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function. - * @method CarbonImmutable floorMonth(float $precision = 1) Truncate the current instance month with given precision. - * @method CarbonImmutable floorMonths(float $precision = 1) Truncate the current instance month with given precision. - * @method CarbonImmutable ceilMonth(float $precision = 1) Ceil the current instance month with given precision. - * @method CarbonImmutable ceilMonths(float $precision = 1) Ceil the current instance month with given precision. - * @method CarbonImmutable roundDay(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. - * @method CarbonImmutable roundDays(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. - * @method CarbonImmutable floorDay(float $precision = 1) Truncate the current instance day with given precision. - * @method CarbonImmutable floorDays(float $precision = 1) Truncate the current instance day with given precision. - * @method CarbonImmutable ceilDay(float $precision = 1) Ceil the current instance day with given precision. - * @method CarbonImmutable ceilDays(float $precision = 1) Ceil the current instance day with given precision. - * @method CarbonImmutable roundHour(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function. - * @method CarbonImmutable roundHours(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function. - * @method CarbonImmutable floorHour(float $precision = 1) Truncate the current instance hour with given precision. - * @method CarbonImmutable floorHours(float $precision = 1) Truncate the current instance hour with given precision. - * @method CarbonImmutable ceilHour(float $precision = 1) Ceil the current instance hour with given precision. - * @method CarbonImmutable ceilHours(float $precision = 1) Ceil the current instance hour with given precision. - * @method CarbonImmutable roundMinute(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function. - * @method CarbonImmutable roundMinutes(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function. - * @method CarbonImmutable floorMinute(float $precision = 1) Truncate the current instance minute with given precision. - * @method CarbonImmutable floorMinutes(float $precision = 1) Truncate the current instance minute with given precision. - * @method CarbonImmutable ceilMinute(float $precision = 1) Ceil the current instance minute with given precision. - * @method CarbonImmutable ceilMinutes(float $precision = 1) Ceil the current instance minute with given precision. - * @method CarbonImmutable roundSecond(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function. - * @method CarbonImmutable roundSeconds(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function. - * @method CarbonImmutable floorSecond(float $precision = 1) Truncate the current instance second with given precision. - * @method CarbonImmutable floorSeconds(float $precision = 1) Truncate the current instance second with given precision. - * @method CarbonImmutable ceilSecond(float $precision = 1) Ceil the current instance second with given precision. - * @method CarbonImmutable ceilSeconds(float $precision = 1) Ceil the current instance second with given precision. - * @method CarbonImmutable roundMillennium(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function. - * @method CarbonImmutable roundMillennia(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function. - * @method CarbonImmutable floorMillennium(float $precision = 1) Truncate the current instance millennium with given precision. - * @method CarbonImmutable floorMillennia(float $precision = 1) Truncate the current instance millennium with given precision. - * @method CarbonImmutable ceilMillennium(float $precision = 1) Ceil the current instance millennium with given precision. - * @method CarbonImmutable ceilMillennia(float $precision = 1) Ceil the current instance millennium with given precision. - * @method CarbonImmutable roundCentury(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function. - * @method CarbonImmutable roundCenturies(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function. - * @method CarbonImmutable floorCentury(float $precision = 1) Truncate the current instance century with given precision. - * @method CarbonImmutable floorCenturies(float $precision = 1) Truncate the current instance century with given precision. - * @method CarbonImmutable ceilCentury(float $precision = 1) Ceil the current instance century with given precision. - * @method CarbonImmutable ceilCenturies(float $precision = 1) Ceil the current instance century with given precision. - * @method CarbonImmutable roundDecade(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function. - * @method CarbonImmutable roundDecades(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function. - * @method CarbonImmutable floorDecade(float $precision = 1) Truncate the current instance decade with given precision. - * @method CarbonImmutable floorDecades(float $precision = 1) Truncate the current instance decade with given precision. - * @method CarbonImmutable ceilDecade(float $precision = 1) Ceil the current instance decade with given precision. - * @method CarbonImmutable ceilDecades(float $precision = 1) Ceil the current instance decade with given precision. - * @method CarbonImmutable roundQuarter(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function. - * @method CarbonImmutable roundQuarters(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function. - * @method CarbonImmutable floorQuarter(float $precision = 1) Truncate the current instance quarter with given precision. - * @method CarbonImmutable floorQuarters(float $precision = 1) Truncate the current instance quarter with given precision. - * @method CarbonImmutable ceilQuarter(float $precision = 1) Ceil the current instance quarter with given precision. - * @method CarbonImmutable ceilQuarters(float $precision = 1) Ceil the current instance quarter with given precision. - * @method CarbonImmutable roundMillisecond(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function. - * @method CarbonImmutable roundMilliseconds(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function. - * @method CarbonImmutable floorMillisecond(float $precision = 1) Truncate the current instance millisecond with given precision. - * @method CarbonImmutable floorMilliseconds(float $precision = 1) Truncate the current instance millisecond with given precision. - * @method CarbonImmutable ceilMillisecond(float $precision = 1) Ceil the current instance millisecond with given precision. - * @method CarbonImmutable ceilMilliseconds(float $precision = 1) Ceil the current instance millisecond with given precision. - * @method CarbonImmutable roundMicrosecond(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function. - * @method CarbonImmutable roundMicroseconds(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function. - * @method CarbonImmutable floorMicrosecond(float $precision = 1) Truncate the current instance microsecond with given precision. - * @method CarbonImmutable floorMicroseconds(float $precision = 1) Truncate the current instance microsecond with given precision. - * @method CarbonImmutable ceilMicrosecond(float $precision = 1) Ceil the current instance microsecond with given precision. - * @method CarbonImmutable ceilMicroseconds(float $precision = 1) Ceil the current instance microsecond with given precision. - * @method string shortAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) - * @method string longAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) - * @method string shortRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) - * @method string longRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) - * @method string shortRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) - * @method string longRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) - * @method string shortRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) - * @method string longRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) - * @method static static|false createFromFormat(string $format, string $time, DateTimeZone|string|false|null $timezone = null) Parse a string into a new CarbonImmutable object according to the specified format. - * @method static static __set_state(array $array) https://php.net/manual/en/datetime.set-state.php + * @method bool isUtc() Check if the current instance has UTC timezone. (Both isUtc and isUTC cases are valid.) + * @method bool isLocal() Check if the current instance has non-UTC timezone. + * @method bool isValid() Check if the current instance is a valid date. + * @method bool isDST() Check if the current instance is in a daylight saving time. + * @method bool isSunday() Checks if the instance day is sunday. + * @method bool isMonday() Checks if the instance day is monday. + * @method bool isTuesday() Checks if the instance day is tuesday. + * @method bool isWednesday() Checks if the instance day is wednesday. + * @method bool isThursday() Checks if the instance day is thursday. + * @method bool isFriday() Checks if the instance day is friday. + * @method bool isSaturday() Checks if the instance day is saturday. + * @method bool isSameYear(DateTimeInterface|string $date) Checks if the given date is in the same year as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentYear() Checks if the instance is in the same year as the current moment. + * @method bool isNextYear() Checks if the instance is in the same year as the current moment next year. + * @method bool isLastYear() Checks if the instance is in the same year as the current moment last year. + * @method bool isCurrentMonth() Checks if the instance is in the same month as the current moment. + * @method bool isNextMonth() Checks if the instance is in the same month as the current moment next month. + * @method bool isLastMonth() Checks if the instance is in the same month as the current moment last month. + * @method bool isSameWeek(DateTimeInterface|string $date) Checks if the given date is in the same week as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentWeek() Checks if the instance is in the same week as the current moment. + * @method bool isNextWeek() Checks if the instance is in the same week as the current moment next week. + * @method bool isLastWeek() Checks if the instance is in the same week as the current moment last week. + * @method bool isSameDay(DateTimeInterface|string $date) Checks if the given date is in the same day as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentDay() Checks if the instance is in the same day as the current moment. + * @method bool isNextDay() Checks if the instance is in the same day as the current moment next day. + * @method bool isLastDay() Checks if the instance is in the same day as the current moment last day. + * @method bool isSameHour(DateTimeInterface|string $date) Checks if the given date is in the same hour as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentHour() Checks if the instance is in the same hour as the current moment. + * @method bool isNextHour() Checks if the instance is in the same hour as the current moment next hour. + * @method bool isLastHour() Checks if the instance is in the same hour as the current moment last hour. + * @method bool isSameMinute(DateTimeInterface|string $date) Checks if the given date is in the same minute as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMinute() Checks if the instance is in the same minute as the current moment. + * @method bool isNextMinute() Checks if the instance is in the same minute as the current moment next minute. + * @method bool isLastMinute() Checks if the instance is in the same minute as the current moment last minute. + * @method bool isSameSecond(DateTimeInterface|string $date) Checks if the given date is in the same second as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentSecond() Checks if the instance is in the same second as the current moment. + * @method bool isNextSecond() Checks if the instance is in the same second as the current moment next second. + * @method bool isLastSecond() Checks if the instance is in the same second as the current moment last second. + * @method bool isSameMilli(DateTimeInterface|string $date) Checks if the given date is in the same millisecond as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMilli() Checks if the instance is in the same millisecond as the current moment. + * @method bool isNextMilli() Checks if the instance is in the same millisecond as the current moment next millisecond. + * @method bool isLastMilli() Checks if the instance is in the same millisecond as the current moment last millisecond. + * @method bool isSameMillisecond(DateTimeInterface|string $date) Checks if the given date is in the same millisecond as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMillisecond() Checks if the instance is in the same millisecond as the current moment. + * @method bool isNextMillisecond() Checks if the instance is in the same millisecond as the current moment next millisecond. + * @method bool isLastMillisecond() Checks if the instance is in the same millisecond as the current moment last millisecond. + * @method bool isSameMicro(DateTimeInterface|string $date) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMicro() Checks if the instance is in the same microsecond as the current moment. + * @method bool isNextMicro() Checks if the instance is in the same microsecond as the current moment next microsecond. + * @method bool isLastMicro() Checks if the instance is in the same microsecond as the current moment last microsecond. + * @method bool isSameMicrosecond(DateTimeInterface|string $date) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMicrosecond() Checks if the instance is in the same microsecond as the current moment. + * @method bool isNextMicrosecond() Checks if the instance is in the same microsecond as the current moment next microsecond. + * @method bool isLastMicrosecond() Checks if the instance is in the same microsecond as the current moment last microsecond. + * @method bool isSameDecade(DateTimeInterface|string $date) Checks if the given date is in the same decade as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentDecade() Checks if the instance is in the same decade as the current moment. + * @method bool isNextDecade() Checks if the instance is in the same decade as the current moment next decade. + * @method bool isLastDecade() Checks if the instance is in the same decade as the current moment last decade. + * @method bool isSameCentury(DateTimeInterface|string $date) Checks if the given date is in the same century as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentCentury() Checks if the instance is in the same century as the current moment. + * @method bool isNextCentury() Checks if the instance is in the same century as the current moment next century. + * @method bool isLastCentury() Checks if the instance is in the same century as the current moment last century. + * @method bool isSameMillennium(DateTimeInterface|string $date) Checks if the given date is in the same millennium as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMillennium() Checks if the instance is in the same millennium as the current moment. + * @method bool isNextMillennium() Checks if the instance is in the same millennium as the current moment next millennium. + * @method bool isLastMillennium() Checks if the instance is in the same millennium as the current moment last millennium. + * @method bool isCurrentQuarter() Checks if the instance is in the same quarter as the current moment. + * @method bool isNextQuarter() Checks if the instance is in the same quarter as the current moment next quarter. + * @method bool isLastQuarter() Checks if the instance is in the same quarter as the current moment last quarter. + * @method CarbonImmutable years(int $value) Set current instance year to the given value. + * @method CarbonImmutable year(int $value) Set current instance year to the given value. + * @method CarbonImmutable setYears(int $value) Set current instance year to the given value. + * @method CarbonImmutable setYear(int $value) Set current instance year to the given value. + * @method CarbonImmutable months(Month|int $value) Set current instance month to the given value. + * @method CarbonImmutable month(Month|int $value) Set current instance month to the given value. + * @method CarbonImmutable setMonths(Month|int $value) Set current instance month to the given value. + * @method CarbonImmutable setMonth(Month|int $value) Set current instance month to the given value. + * @method CarbonImmutable days(int $value) Set current instance day to the given value. + * @method CarbonImmutable day(int $value) Set current instance day to the given value. + * @method CarbonImmutable setDays(int $value) Set current instance day to the given value. + * @method CarbonImmutable setDay(int $value) Set current instance day to the given value. + * @method CarbonImmutable hours(int $value) Set current instance hour to the given value. + * @method CarbonImmutable hour(int $value) Set current instance hour to the given value. + * @method CarbonImmutable setHours(int $value) Set current instance hour to the given value. + * @method CarbonImmutable setHour(int $value) Set current instance hour to the given value. + * @method CarbonImmutable minutes(int $value) Set current instance minute to the given value. + * @method CarbonImmutable minute(int $value) Set current instance minute to the given value. + * @method CarbonImmutable setMinutes(int $value) Set current instance minute to the given value. + * @method CarbonImmutable setMinute(int $value) Set current instance minute to the given value. + * @method CarbonImmutable seconds(int $value) Set current instance second to the given value. + * @method CarbonImmutable second(int $value) Set current instance second to the given value. + * @method CarbonImmutable setSeconds(int $value) Set current instance second to the given value. + * @method CarbonImmutable setSecond(int $value) Set current instance second to the given value. + * @method CarbonImmutable millis(int $value) Set current instance millisecond to the given value. + * @method CarbonImmutable milli(int $value) Set current instance millisecond to the given value. + * @method CarbonImmutable setMillis(int $value) Set current instance millisecond to the given value. + * @method CarbonImmutable setMilli(int $value) Set current instance millisecond to the given value. + * @method CarbonImmutable milliseconds(int $value) Set current instance millisecond to the given value. + * @method CarbonImmutable millisecond(int $value) Set current instance millisecond to the given value. + * @method CarbonImmutable setMilliseconds(int $value) Set current instance millisecond to the given value. + * @method CarbonImmutable setMillisecond(int $value) Set current instance millisecond to the given value. + * @method CarbonImmutable micros(int $value) Set current instance microsecond to the given value. + * @method CarbonImmutable micro(int $value) Set current instance microsecond to the given value. + * @method CarbonImmutable setMicros(int $value) Set current instance microsecond to the given value. + * @method CarbonImmutable setMicro(int $value) Set current instance microsecond to the given value. + * @method CarbonImmutable microseconds(int $value) Set current instance microsecond to the given value. + * @method CarbonImmutable microsecond(int $value) Set current instance microsecond to the given value. + * @method CarbonImmutable setMicroseconds(int $value) Set current instance microsecond to the given value. + * @method CarbonImmutable setMicrosecond(int $value) Set current instance microsecond to the given value. + * @method CarbonImmutable addYears(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addYear() Add one year to the instance (using date interval). + * @method CarbonImmutable subYears(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subYear() Sub one year to the instance (using date interval). + * @method CarbonImmutable addYearsWithOverflow(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable addYearWithOverflow() Add one year to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable subYearsWithOverflow(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable subYearWithOverflow() Sub one year to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable addYearsWithoutOverflow(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addYearWithoutOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subYearsWithoutOverflow(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subYearWithoutOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addYearsWithNoOverflow(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addYearWithNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subYearsWithNoOverflow(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subYearWithNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addYearsNoOverflow(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addYearNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subYearsNoOverflow(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subYearNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addMonths(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addMonth() Add one month to the instance (using date interval). + * @method CarbonImmutable subMonths(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subMonth() Sub one month to the instance (using date interval). + * @method CarbonImmutable addMonthsWithOverflow(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable addMonthWithOverflow() Add one month to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable subMonthsWithOverflow(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable subMonthWithOverflow() Sub one month to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable addMonthsWithoutOverflow(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addMonthWithoutOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subMonthsWithoutOverflow(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subMonthWithoutOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addMonthsWithNoOverflow(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addMonthWithNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subMonthsWithNoOverflow(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subMonthWithNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addMonthsNoOverflow(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addMonthNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subMonthsNoOverflow(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subMonthNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addDays(int|float $value = 1) Add days (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addDay() Add one day to the instance (using date interval). + * @method CarbonImmutable subDays(int|float $value = 1) Sub days (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subDay() Sub one day to the instance (using date interval). + * @method CarbonImmutable addHours(int|float $value = 1) Add hours (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addHour() Add one hour to the instance (using date interval). + * @method CarbonImmutable subHours(int|float $value = 1) Sub hours (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subHour() Sub one hour to the instance (using date interval). + * @method CarbonImmutable addMinutes(int|float $value = 1) Add minutes (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addMinute() Add one minute to the instance (using date interval). + * @method CarbonImmutable subMinutes(int|float $value = 1) Sub minutes (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subMinute() Sub one minute to the instance (using date interval). + * @method CarbonImmutable addSeconds(int|float $value = 1) Add seconds (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addSecond() Add one second to the instance (using date interval). + * @method CarbonImmutable subSeconds(int|float $value = 1) Sub seconds (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subSecond() Sub one second to the instance (using date interval). + * @method CarbonImmutable addMillis(int|float $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addMilli() Add one millisecond to the instance (using date interval). + * @method CarbonImmutable subMillis(int|float $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subMilli() Sub one millisecond to the instance (using date interval). + * @method CarbonImmutable addMilliseconds(int|float $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addMillisecond() Add one millisecond to the instance (using date interval). + * @method CarbonImmutable subMilliseconds(int|float $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subMillisecond() Sub one millisecond to the instance (using date interval). + * @method CarbonImmutable addMicros(int|float $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addMicro() Add one microsecond to the instance (using date interval). + * @method CarbonImmutable subMicros(int|float $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subMicro() Sub one microsecond to the instance (using date interval). + * @method CarbonImmutable addMicroseconds(int|float $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addMicrosecond() Add one microsecond to the instance (using date interval). + * @method CarbonImmutable subMicroseconds(int|float $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subMicrosecond() Sub one microsecond to the instance (using date interval). + * @method CarbonImmutable addMillennia(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addMillennium() Add one millennium to the instance (using date interval). + * @method CarbonImmutable subMillennia(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subMillennium() Sub one millennium to the instance (using date interval). + * @method CarbonImmutable addMillenniaWithOverflow(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable addMillenniumWithOverflow() Add one millennium to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable subMillenniaWithOverflow(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable subMillenniumWithOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable addMillenniaWithoutOverflow(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addMillenniumWithoutOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subMillenniaWithoutOverflow(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subMillenniumWithoutOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addMillenniaWithNoOverflow(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addMillenniumWithNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subMillenniaWithNoOverflow(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subMillenniumWithNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addMillenniaNoOverflow(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addMillenniumNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subMillenniaNoOverflow(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subMillenniumNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addCenturies(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addCentury() Add one century to the instance (using date interval). + * @method CarbonImmutable subCenturies(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subCentury() Sub one century to the instance (using date interval). + * @method CarbonImmutable addCenturiesWithOverflow(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable addCenturyWithOverflow() Add one century to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable subCenturiesWithOverflow(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable subCenturyWithOverflow() Sub one century to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable addCenturiesWithoutOverflow(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addCenturyWithoutOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subCenturiesWithoutOverflow(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subCenturyWithoutOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addCenturiesWithNoOverflow(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addCenturyWithNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subCenturiesWithNoOverflow(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subCenturyWithNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addCenturiesNoOverflow(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addCenturyNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subCenturiesNoOverflow(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subCenturyNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addDecades(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addDecade() Add one decade to the instance (using date interval). + * @method CarbonImmutable subDecades(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subDecade() Sub one decade to the instance (using date interval). + * @method CarbonImmutable addDecadesWithOverflow(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable addDecadeWithOverflow() Add one decade to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable subDecadesWithOverflow(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable subDecadeWithOverflow() Sub one decade to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable addDecadesWithoutOverflow(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addDecadeWithoutOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subDecadesWithoutOverflow(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subDecadeWithoutOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addDecadesWithNoOverflow(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addDecadeWithNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subDecadesWithNoOverflow(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subDecadeWithNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addDecadesNoOverflow(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addDecadeNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subDecadesNoOverflow(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subDecadeNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addQuarters(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addQuarter() Add one quarter to the instance (using date interval). + * @method CarbonImmutable subQuarters(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subQuarter() Sub one quarter to the instance (using date interval). + * @method CarbonImmutable addQuartersWithOverflow(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable addQuarterWithOverflow() Add one quarter to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable subQuartersWithOverflow(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable subQuarterWithOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable addQuartersWithoutOverflow(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addQuarterWithoutOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subQuartersWithoutOverflow(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subQuarterWithoutOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addQuartersWithNoOverflow(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addQuarterWithNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subQuartersWithNoOverflow(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subQuarterWithNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addQuartersNoOverflow(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addQuarterNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subQuartersNoOverflow(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subQuarterNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addWeeks(int|float $value = 1) Add weeks (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addWeek() Add one week to the instance (using date interval). + * @method CarbonImmutable subWeeks(int|float $value = 1) Sub weeks (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subWeek() Sub one week to the instance (using date interval). + * @method CarbonImmutable addWeekdays(int|float $value = 1) Add weekdays (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addWeekday() Add one weekday to the instance (using date interval). + * @method CarbonImmutable subWeekdays(int|float $value = 1) Sub weekdays (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subWeekday() Sub one weekday to the instance (using date interval). + * @method CarbonImmutable addUTCMicros(int|float $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addUTCMicro() Add one microsecond to the instance (using timestamp). + * @method CarbonImmutable subUTCMicros(int|float $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subUTCMicro() Sub one microsecond to the instance (using timestamp). + * @method CarbonPeriod microsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. + * @method float diffInUTCMicros(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of microseconds. + * @method CarbonImmutable addUTCMicroseconds(int|float $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addUTCMicrosecond() Add one microsecond to the instance (using timestamp). + * @method CarbonImmutable subUTCMicroseconds(int|float $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subUTCMicrosecond() Sub one microsecond to the instance (using timestamp). + * @method CarbonPeriod microsecondsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. + * @method float diffInUTCMicroseconds(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of microseconds. + * @method CarbonImmutable addUTCMillis(int|float $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addUTCMilli() Add one millisecond to the instance (using timestamp). + * @method CarbonImmutable subUTCMillis(int|float $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subUTCMilli() Sub one millisecond to the instance (using timestamp). + * @method CarbonPeriod millisUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. + * @method float diffInUTCMillis(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of milliseconds. + * @method CarbonImmutable addUTCMilliseconds(int|float $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addUTCMillisecond() Add one millisecond to the instance (using timestamp). + * @method CarbonImmutable subUTCMilliseconds(int|float $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subUTCMillisecond() Sub one millisecond to the instance (using timestamp). + * @method CarbonPeriod millisecondsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. + * @method float diffInUTCMilliseconds(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of milliseconds. + * @method CarbonImmutable addUTCSeconds(int|float $value = 1) Add seconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addUTCSecond() Add one second to the instance (using timestamp). + * @method CarbonImmutable subUTCSeconds(int|float $value = 1) Sub seconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subUTCSecond() Sub one second to the instance (using timestamp). + * @method CarbonPeriod secondsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each second or every X seconds if a factor is given. + * @method float diffInUTCSeconds(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of seconds. + * @method CarbonImmutable addUTCMinutes(int|float $value = 1) Add minutes (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addUTCMinute() Add one minute to the instance (using timestamp). + * @method CarbonImmutable subUTCMinutes(int|float $value = 1) Sub minutes (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subUTCMinute() Sub one minute to the instance (using timestamp). + * @method CarbonPeriod minutesUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each minute or every X minutes if a factor is given. + * @method float diffInUTCMinutes(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of minutes. + * @method CarbonImmutable addUTCHours(int|float $value = 1) Add hours (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addUTCHour() Add one hour to the instance (using timestamp). + * @method CarbonImmutable subUTCHours(int|float $value = 1) Sub hours (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subUTCHour() Sub one hour to the instance (using timestamp). + * @method CarbonPeriod hoursUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each hour or every X hours if a factor is given. + * @method float diffInUTCHours(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of hours. + * @method CarbonImmutable addUTCDays(int|float $value = 1) Add days (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addUTCDay() Add one day to the instance (using timestamp). + * @method CarbonImmutable subUTCDays(int|float $value = 1) Sub days (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subUTCDay() Sub one day to the instance (using timestamp). + * @method CarbonPeriod daysUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each day or every X days if a factor is given. + * @method float diffInUTCDays(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of days. + * @method CarbonImmutable addUTCWeeks(int|float $value = 1) Add weeks (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addUTCWeek() Add one week to the instance (using timestamp). + * @method CarbonImmutable subUTCWeeks(int|float $value = 1) Sub weeks (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subUTCWeek() Sub one week to the instance (using timestamp). + * @method CarbonPeriod weeksUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each week or every X weeks if a factor is given. + * @method float diffInUTCWeeks(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of weeks. + * @method CarbonImmutable addUTCMonths(int|float $value = 1) Add months (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addUTCMonth() Add one month to the instance (using timestamp). + * @method CarbonImmutable subUTCMonths(int|float $value = 1) Sub months (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subUTCMonth() Sub one month to the instance (using timestamp). + * @method CarbonPeriod monthsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each month or every X months if a factor is given. + * @method float diffInUTCMonths(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of months. + * @method CarbonImmutable addUTCQuarters(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addUTCQuarter() Add one quarter to the instance (using timestamp). + * @method CarbonImmutable subUTCQuarters(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subUTCQuarter() Sub one quarter to the instance (using timestamp). + * @method CarbonPeriod quartersUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each quarter or every X quarters if a factor is given. + * @method float diffInUTCQuarters(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of quarters. + * @method CarbonImmutable addUTCYears(int|float $value = 1) Add years (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addUTCYear() Add one year to the instance (using timestamp). + * @method CarbonImmutable subUTCYears(int|float $value = 1) Sub years (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subUTCYear() Sub one year to the instance (using timestamp). + * @method CarbonPeriod yearsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each year or every X years if a factor is given. + * @method float diffInUTCYears(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of years. + * @method CarbonImmutable addUTCDecades(int|float $value = 1) Add decades (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addUTCDecade() Add one decade to the instance (using timestamp). + * @method CarbonImmutable subUTCDecades(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subUTCDecade() Sub one decade to the instance (using timestamp). + * @method CarbonPeriod decadesUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each decade or every X decades if a factor is given. + * @method float diffInUTCDecades(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of decades. + * @method CarbonImmutable addUTCCenturies(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addUTCCentury() Add one century to the instance (using timestamp). + * @method CarbonImmutable subUTCCenturies(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subUTCCentury() Sub one century to the instance (using timestamp). + * @method CarbonPeriod centuriesUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each century or every X centuries if a factor is given. + * @method float diffInUTCCenturies(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of centuries. + * @method CarbonImmutable addUTCMillennia(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addUTCMillennium() Add one millennium to the instance (using timestamp). + * @method CarbonImmutable subUTCMillennia(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subUTCMillennium() Sub one millennium to the instance (using timestamp). + * @method CarbonPeriod millenniaUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millennium or every X millennia if a factor is given. + * @method float diffInUTCMillennia(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of millennia. + * @method CarbonImmutable roundYear(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. + * @method CarbonImmutable roundYears(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. + * @method CarbonImmutable floorYear(float $precision = 1) Truncate the current instance year with given precision. + * @method CarbonImmutable floorYears(float $precision = 1) Truncate the current instance year with given precision. + * @method CarbonImmutable ceilYear(float $precision = 1) Ceil the current instance year with given precision. + * @method CarbonImmutable ceilYears(float $precision = 1) Ceil the current instance year with given precision. + * @method CarbonImmutable roundMonth(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function. + * @method CarbonImmutable roundMonths(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function. + * @method CarbonImmutable floorMonth(float $precision = 1) Truncate the current instance month with given precision. + * @method CarbonImmutable floorMonths(float $precision = 1) Truncate the current instance month with given precision. + * @method CarbonImmutable ceilMonth(float $precision = 1) Ceil the current instance month with given precision. + * @method CarbonImmutable ceilMonths(float $precision = 1) Ceil the current instance month with given precision. + * @method CarbonImmutable roundDay(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. + * @method CarbonImmutable roundDays(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. + * @method CarbonImmutable floorDay(float $precision = 1) Truncate the current instance day with given precision. + * @method CarbonImmutable floorDays(float $precision = 1) Truncate the current instance day with given precision. + * @method CarbonImmutable ceilDay(float $precision = 1) Ceil the current instance day with given precision. + * @method CarbonImmutable ceilDays(float $precision = 1) Ceil the current instance day with given precision. + * @method CarbonImmutable roundHour(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function. + * @method CarbonImmutable roundHours(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function. + * @method CarbonImmutable floorHour(float $precision = 1) Truncate the current instance hour with given precision. + * @method CarbonImmutable floorHours(float $precision = 1) Truncate the current instance hour with given precision. + * @method CarbonImmutable ceilHour(float $precision = 1) Ceil the current instance hour with given precision. + * @method CarbonImmutable ceilHours(float $precision = 1) Ceil the current instance hour with given precision. + * @method CarbonImmutable roundMinute(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function. + * @method CarbonImmutable roundMinutes(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function. + * @method CarbonImmutable floorMinute(float $precision = 1) Truncate the current instance minute with given precision. + * @method CarbonImmutable floorMinutes(float $precision = 1) Truncate the current instance minute with given precision. + * @method CarbonImmutable ceilMinute(float $precision = 1) Ceil the current instance minute with given precision. + * @method CarbonImmutable ceilMinutes(float $precision = 1) Ceil the current instance minute with given precision. + * @method CarbonImmutable roundSecond(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function. + * @method CarbonImmutable roundSeconds(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function. + * @method CarbonImmutable floorSecond(float $precision = 1) Truncate the current instance second with given precision. + * @method CarbonImmutable floorSeconds(float $precision = 1) Truncate the current instance second with given precision. + * @method CarbonImmutable ceilSecond(float $precision = 1) Ceil the current instance second with given precision. + * @method CarbonImmutable ceilSeconds(float $precision = 1) Ceil the current instance second with given precision. + * @method CarbonImmutable roundMillennium(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function. + * @method CarbonImmutable roundMillennia(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function. + * @method CarbonImmutable floorMillennium(float $precision = 1) Truncate the current instance millennium with given precision. + * @method CarbonImmutable floorMillennia(float $precision = 1) Truncate the current instance millennium with given precision. + * @method CarbonImmutable ceilMillennium(float $precision = 1) Ceil the current instance millennium with given precision. + * @method CarbonImmutable ceilMillennia(float $precision = 1) Ceil the current instance millennium with given precision. + * @method CarbonImmutable roundCentury(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function. + * @method CarbonImmutable roundCenturies(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function. + * @method CarbonImmutable floorCentury(float $precision = 1) Truncate the current instance century with given precision. + * @method CarbonImmutable floorCenturies(float $precision = 1) Truncate the current instance century with given precision. + * @method CarbonImmutable ceilCentury(float $precision = 1) Ceil the current instance century with given precision. + * @method CarbonImmutable ceilCenturies(float $precision = 1) Ceil the current instance century with given precision. + * @method CarbonImmutable roundDecade(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function. + * @method CarbonImmutable roundDecades(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function. + * @method CarbonImmutable floorDecade(float $precision = 1) Truncate the current instance decade with given precision. + * @method CarbonImmutable floorDecades(float $precision = 1) Truncate the current instance decade with given precision. + * @method CarbonImmutable ceilDecade(float $precision = 1) Ceil the current instance decade with given precision. + * @method CarbonImmutable ceilDecades(float $precision = 1) Ceil the current instance decade with given precision. + * @method CarbonImmutable roundQuarter(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function. + * @method CarbonImmutable roundQuarters(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function. + * @method CarbonImmutable floorQuarter(float $precision = 1) Truncate the current instance quarter with given precision. + * @method CarbonImmutable floorQuarters(float $precision = 1) Truncate the current instance quarter with given precision. + * @method CarbonImmutable ceilQuarter(float $precision = 1) Ceil the current instance quarter with given precision. + * @method CarbonImmutable ceilQuarters(float $precision = 1) Ceil the current instance quarter with given precision. + * @method CarbonImmutable roundMillisecond(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function. + * @method CarbonImmutable roundMilliseconds(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function. + * @method CarbonImmutable floorMillisecond(float $precision = 1) Truncate the current instance millisecond with given precision. + * @method CarbonImmutable floorMilliseconds(float $precision = 1) Truncate the current instance millisecond with given precision. + * @method CarbonImmutable ceilMillisecond(float $precision = 1) Ceil the current instance millisecond with given precision. + * @method CarbonImmutable ceilMilliseconds(float $precision = 1) Ceil the current instance millisecond with given precision. + * @method CarbonImmutable roundMicrosecond(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function. + * @method CarbonImmutable roundMicroseconds(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function. + * @method CarbonImmutable floorMicrosecond(float $precision = 1) Truncate the current instance microsecond with given precision. + * @method CarbonImmutable floorMicroseconds(float $precision = 1) Truncate the current instance microsecond with given precision. + * @method CarbonImmutable ceilMicrosecond(float $precision = 1) Ceil the current instance microsecond with given precision. + * @method CarbonImmutable ceilMicroseconds(float $precision = 1) Ceil the current instance microsecond with given precision. + * @method string shortAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string longAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string shortRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string longRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string shortRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string longRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string shortRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string longRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method int centuriesInMillennium() Return the number of centuries contained in the current millennium + * @method int|static centuryOfMillennium(?int $century = null) Return the value of the century starting from the beginning of the current millennium when called with no parameters, change the current century when called with an integer value + * @method int|static dayOfCentury(?int $day = null) Return the value of the day starting from the beginning of the current century when called with no parameters, change the current day when called with an integer value + * @method int|static dayOfDecade(?int $day = null) Return the value of the day starting from the beginning of the current decade when called with no parameters, change the current day when called with an integer value + * @method int|static dayOfMillennium(?int $day = null) Return the value of the day starting from the beginning of the current millennium when called with no parameters, change the current day when called with an integer value + * @method int|static dayOfMonth(?int $day = null) Return the value of the day starting from the beginning of the current month when called with no parameters, change the current day when called with an integer value + * @method int|static dayOfQuarter(?int $day = null) Return the value of the day starting from the beginning of the current quarter when called with no parameters, change the current day when called with an integer value + * @method int|static dayOfWeek(?int $day = null) Return the value of the day starting from the beginning of the current week when called with no parameters, change the current day when called with an integer value + * @method int daysInCentury() Return the number of days contained in the current century + * @method int daysInDecade() Return the number of days contained in the current decade + * @method int daysInMillennium() Return the number of days contained in the current millennium + * @method int daysInMonth() Return the number of days contained in the current month + * @method int daysInQuarter() Return the number of days contained in the current quarter + * @method int daysInWeek() Return the number of days contained in the current week + * @method int daysInYear() Return the number of days contained in the current year + * @method int|static decadeOfCentury(?int $decade = null) Return the value of the decade starting from the beginning of the current century when called with no parameters, change the current decade when called with an integer value + * @method int|static decadeOfMillennium(?int $decade = null) Return the value of the decade starting from the beginning of the current millennium when called with no parameters, change the current decade when called with an integer value + * @method int decadesInCentury() Return the number of decades contained in the current century + * @method int decadesInMillennium() Return the number of decades contained in the current millennium + * @method int|static hourOfCentury(?int $hour = null) Return the value of the hour starting from the beginning of the current century when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfDay(?int $hour = null) Return the value of the hour starting from the beginning of the current day when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfDecade(?int $hour = null) Return the value of the hour starting from the beginning of the current decade when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfMillennium(?int $hour = null) Return the value of the hour starting from the beginning of the current millennium when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfMonth(?int $hour = null) Return the value of the hour starting from the beginning of the current month when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfQuarter(?int $hour = null) Return the value of the hour starting from the beginning of the current quarter when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfWeek(?int $hour = null) Return the value of the hour starting from the beginning of the current week when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfYear(?int $hour = null) Return the value of the hour starting from the beginning of the current year when called with no parameters, change the current hour when called with an integer value + * @method int hoursInCentury() Return the number of hours contained in the current century + * @method int hoursInDay() Return the number of hours contained in the current day + * @method int hoursInDecade() Return the number of hours contained in the current decade + * @method int hoursInMillennium() Return the number of hours contained in the current millennium + * @method int hoursInMonth() Return the number of hours contained in the current month + * @method int hoursInQuarter() Return the number of hours contained in the current quarter + * @method int hoursInWeek() Return the number of hours contained in the current week + * @method int hoursInYear() Return the number of hours contained in the current year + * @method int|static microsecondOfCentury(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current century when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfDay(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current day when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfDecade(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current decade when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfHour(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current hour when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfMillennium(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current millennium when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfMillisecond(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current millisecond when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfMinute(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current minute when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfMonth(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current month when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfQuarter(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current quarter when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfSecond(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current second when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfWeek(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current week when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfYear(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current year when called with no parameters, change the current microsecond when called with an integer value + * @method int microsecondsInCentury() Return the number of microseconds contained in the current century + * @method int microsecondsInDay() Return the number of microseconds contained in the current day + * @method int microsecondsInDecade() Return the number of microseconds contained in the current decade + * @method int microsecondsInHour() Return the number of microseconds contained in the current hour + * @method int microsecondsInMillennium() Return the number of microseconds contained in the current millennium + * @method int microsecondsInMillisecond() Return the number of microseconds contained in the current millisecond + * @method int microsecondsInMinute() Return the number of microseconds contained in the current minute + * @method int microsecondsInMonth() Return the number of microseconds contained in the current month + * @method int microsecondsInQuarter() Return the number of microseconds contained in the current quarter + * @method int microsecondsInSecond() Return the number of microseconds contained in the current second + * @method int microsecondsInWeek() Return the number of microseconds contained in the current week + * @method int microsecondsInYear() Return the number of microseconds contained in the current year + * @method int|static millisecondOfCentury(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current century when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfDay(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current day when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfDecade(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current decade when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfHour(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current hour when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfMillennium(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current millennium when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfMinute(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current minute when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfMonth(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current month when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfQuarter(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current quarter when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfSecond(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current second when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfWeek(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current week when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfYear(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current year when called with no parameters, change the current millisecond when called with an integer value + * @method int millisecondsInCentury() Return the number of milliseconds contained in the current century + * @method int millisecondsInDay() Return the number of milliseconds contained in the current day + * @method int millisecondsInDecade() Return the number of milliseconds contained in the current decade + * @method int millisecondsInHour() Return the number of milliseconds contained in the current hour + * @method int millisecondsInMillennium() Return the number of milliseconds contained in the current millennium + * @method int millisecondsInMinute() Return the number of milliseconds contained in the current minute + * @method int millisecondsInMonth() Return the number of milliseconds contained in the current month + * @method int millisecondsInQuarter() Return the number of milliseconds contained in the current quarter + * @method int millisecondsInSecond() Return the number of milliseconds contained in the current second + * @method int millisecondsInWeek() Return the number of milliseconds contained in the current week + * @method int millisecondsInYear() Return the number of milliseconds contained in the current year + * @method int|static minuteOfCentury(?int $minute = null) Return the value of the minute starting from the beginning of the current century when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfDay(?int $minute = null) Return the value of the minute starting from the beginning of the current day when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfDecade(?int $minute = null) Return the value of the minute starting from the beginning of the current decade when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfHour(?int $minute = null) Return the value of the minute starting from the beginning of the current hour when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfMillennium(?int $minute = null) Return the value of the minute starting from the beginning of the current millennium when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfMonth(?int $minute = null) Return the value of the minute starting from the beginning of the current month when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfQuarter(?int $minute = null) Return the value of the minute starting from the beginning of the current quarter when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfWeek(?int $minute = null) Return the value of the minute starting from the beginning of the current week when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfYear(?int $minute = null) Return the value of the minute starting from the beginning of the current year when called with no parameters, change the current minute when called with an integer value + * @method int minutesInCentury() Return the number of minutes contained in the current century + * @method int minutesInDay() Return the number of minutes contained in the current day + * @method int minutesInDecade() Return the number of minutes contained in the current decade + * @method int minutesInHour() Return the number of minutes contained in the current hour + * @method int minutesInMillennium() Return the number of minutes contained in the current millennium + * @method int minutesInMonth() Return the number of minutes contained in the current month + * @method int minutesInQuarter() Return the number of minutes contained in the current quarter + * @method int minutesInWeek() Return the number of minutes contained in the current week + * @method int minutesInYear() Return the number of minutes contained in the current year + * @method int|static monthOfCentury(?int $month = null) Return the value of the month starting from the beginning of the current century when called with no parameters, change the current month when called with an integer value + * @method int|static monthOfDecade(?int $month = null) Return the value of the month starting from the beginning of the current decade when called with no parameters, change the current month when called with an integer value + * @method int|static monthOfMillennium(?int $month = null) Return the value of the month starting from the beginning of the current millennium when called with no parameters, change the current month when called with an integer value + * @method int|static monthOfQuarter(?int $month = null) Return the value of the month starting from the beginning of the current quarter when called with no parameters, change the current month when called with an integer value + * @method int|static monthOfYear(?int $month = null) Return the value of the month starting from the beginning of the current year when called with no parameters, change the current month when called with an integer value + * @method int monthsInCentury() Return the number of months contained in the current century + * @method int monthsInDecade() Return the number of months contained in the current decade + * @method int monthsInMillennium() Return the number of months contained in the current millennium + * @method int monthsInQuarter() Return the number of months contained in the current quarter + * @method int monthsInYear() Return the number of months contained in the current year + * @method int|static quarterOfCentury(?int $quarter = null) Return the value of the quarter starting from the beginning of the current century when called with no parameters, change the current quarter when called with an integer value + * @method int|static quarterOfDecade(?int $quarter = null) Return the value of the quarter starting from the beginning of the current decade when called with no parameters, change the current quarter when called with an integer value + * @method int|static quarterOfMillennium(?int $quarter = null) Return the value of the quarter starting from the beginning of the current millennium when called with no parameters, change the current quarter when called with an integer value + * @method int|static quarterOfYear(?int $quarter = null) Return the value of the quarter starting from the beginning of the current year when called with no parameters, change the current quarter when called with an integer value + * @method int quartersInCentury() Return the number of quarters contained in the current century + * @method int quartersInDecade() Return the number of quarters contained in the current decade + * @method int quartersInMillennium() Return the number of quarters contained in the current millennium + * @method int quartersInYear() Return the number of quarters contained in the current year + * @method int|static secondOfCentury(?int $second = null) Return the value of the second starting from the beginning of the current century when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfDay(?int $second = null) Return the value of the second starting from the beginning of the current day when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfDecade(?int $second = null) Return the value of the second starting from the beginning of the current decade when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfHour(?int $second = null) Return the value of the second starting from the beginning of the current hour when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfMillennium(?int $second = null) Return the value of the second starting from the beginning of the current millennium when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfMinute(?int $second = null) Return the value of the second starting from the beginning of the current minute when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfMonth(?int $second = null) Return the value of the second starting from the beginning of the current month when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfQuarter(?int $second = null) Return the value of the second starting from the beginning of the current quarter when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfWeek(?int $second = null) Return the value of the second starting from the beginning of the current week when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfYear(?int $second = null) Return the value of the second starting from the beginning of the current year when called with no parameters, change the current second when called with an integer value + * @method int secondsInCentury() Return the number of seconds contained in the current century + * @method int secondsInDay() Return the number of seconds contained in the current day + * @method int secondsInDecade() Return the number of seconds contained in the current decade + * @method int secondsInHour() Return the number of seconds contained in the current hour + * @method int secondsInMillennium() Return the number of seconds contained in the current millennium + * @method int secondsInMinute() Return the number of seconds contained in the current minute + * @method int secondsInMonth() Return the number of seconds contained in the current month + * @method int secondsInQuarter() Return the number of seconds contained in the current quarter + * @method int secondsInWeek() Return the number of seconds contained in the current week + * @method int secondsInYear() Return the number of seconds contained in the current year + * @method int|static weekOfCentury(?int $week = null) Return the value of the week starting from the beginning of the current century when called with no parameters, change the current week when called with an integer value + * @method int|static weekOfDecade(?int $week = null) Return the value of the week starting from the beginning of the current decade when called with no parameters, change the current week when called with an integer value + * @method int|static weekOfMillennium(?int $week = null) Return the value of the week starting from the beginning of the current millennium when called with no parameters, change the current week when called with an integer value + * @method int|static weekOfMonth(?int $week = null) Return the value of the week starting from the beginning of the current month when called with no parameters, change the current week when called with an integer value + * @method int|static weekOfQuarter(?int $week = null) Return the value of the week starting from the beginning of the current quarter when called with no parameters, change the current week when called with an integer value + * @method int|static weekOfYear(?int $week = null) Return the value of the week starting from the beginning of the current year when called with no parameters, change the current week when called with an integer value + * @method int weeksInCentury() Return the number of weeks contained in the current century + * @method int weeksInDecade() Return the number of weeks contained in the current decade + * @method int weeksInMillennium() Return the number of weeks contained in the current millennium + * @method int weeksInMonth() Return the number of weeks contained in the current month + * @method int weeksInQuarter() Return the number of weeks contained in the current quarter + * @method int|static yearOfCentury(?int $year = null) Return the value of the year starting from the beginning of the current century when called with no parameters, change the current year when called with an integer value + * @method int|static yearOfDecade(?int $year = null) Return the value of the year starting from the beginning of the current decade when called with no parameters, change the current year when called with an integer value + * @method int|static yearOfMillennium(?int $year = null) Return the value of the year starting from the beginning of the current millennium when called with no parameters, change the current year when called with an integer value + * @method int yearsInCentury() Return the number of years contained in the current century + * @method int yearsInDecade() Return the number of years contained in the current decade + * @method int yearsInMillennium() Return the number of years contained in the current millennium * * */ @@ -513,7 +839,7 @@ class CarbonImmutable extends DateTimeImmutable implements CarbonInterface __clone as dateTraitClone; } - public function __clone() + public function __clone(): void { $this->dateTraitClone(); $this->endOfTime = false; @@ -525,7 +851,7 @@ public function __clone() * * @return static */ - public static function startOfTime(): self + public static function startOfTime(): static { $date = static::parse('0001-01-01')->years(self::getStartOfTimeYear()); $date->startOfTime = true; @@ -538,7 +864,7 @@ public static function startOfTime(): self * * @return static */ - public static function endOfTime(): self + public static function endOfTime(): static { $date = static::parse('9999-12-31 23:59:59.999999')->years(self::getEndOfTimeYear()); $date->endOfTime = true; @@ -551,16 +877,7 @@ public static function endOfTime(): self */ private static function getEndOfTimeYear(): int { - if (version_compare(PHP_VERSION, '7.3.0-dev', '<')) { - return 145261681241552; - } - - // Remove if https://bugs.php.net/bug.php?id=81107 is fixed - if (version_compare(PHP_VERSION, '8.1.0-dev', '>=')) { - return 1118290769066902787; - } - - return PHP_INT_MAX; + return 1118290769066902787; // PHP_INT_MAX no longer work since PHP 8.1 } /** @@ -568,15 +885,6 @@ private static function getEndOfTimeYear(): int */ private static function getStartOfTimeYear(): int { - if (version_compare(PHP_VERSION, '7.3.0-dev', '<')) { - return -135908816449551; - } - - // Remove if https://bugs.php.net/bug.php?id=81107 is fixed - if (version_compare(PHP_VERSION, '8.1.0-dev', '>=')) { - return -1118290769066898816; - } - - return max(PHP_INT_MIN, -9223372036854773760); + return -1118290769066898816; // PHP_INT_MIN no longer work since PHP 8.1 } } diff --git a/vendor/nesbot/carbon/src/Carbon/CarbonInterface.php b/vendor/nesbot/carbon/src/Carbon/CarbonInterface.php index b90e29817..1779246e5 100644 --- a/vendor/nesbot/carbon/src/Carbon/CarbonInterface.php +++ b/vendor/nesbot/carbon/src/Carbon/CarbonInterface.php @@ -1,5 +1,7 @@ * + * @property string $localeDayOfWeek the day of week in current locale + * @property string $shortLocaleDayOfWeek the abbreviated day of week in current locale + * @property string $localeMonth the month in current locale + * @property string $shortLocaleMonth the abbreviated month in current locale * @property int $year * @property int $yearIso * @property int $month @@ -45,6 +51,7 @@ * @property int $second * @property int $micro * @property int $microsecond + * @property int $dayOfWeekIso 1 (for Monday) through 7 (for Sunday) * @property int|float|string $timestamp seconds since the Unix Epoch * @property string $englishDayOfWeek the day of week in English * @property string $shortEnglishDayOfWeek the abbreviated day of week in English @@ -57,17 +64,90 @@ * @property int $isoWeek 1 through 53 * @property int $weekYear year according to week format * @property int $isoWeekYear year according to ISO week format - * @property int $dayOfYear 1 through 366 * @property int $age does a diffInYears() with default parameters * @property int $offset the timezone offset in seconds from UTC * @property int $offsetMinutes the timezone offset in minutes from UTC * @property int $offsetHours the timezone offset in hours from UTC * @property CarbonTimeZone $timezone the current timezone * @property CarbonTimeZone $tz alias of $timezone - * @property-read int $dayOfWeek 0 (for Sunday) through 6 (for Saturday) - * @property-read int $dayOfWeekIso 1 (for Monday) through 7 (for Sunday) - * @property-read int $weekOfYear ISO-8601 week number of year, weeks starting on Monday - * @property-read int $daysInMonth number of days in the given month + * @property int $centuryOfMillennium The value of the century starting from the beginning of the current millennium + * @property int $dayOfCentury The value of the day starting from the beginning of the current century + * @property int $dayOfDecade The value of the day starting from the beginning of the current decade + * @property int $dayOfMillennium The value of the day starting from the beginning of the current millennium + * @property int $dayOfMonth The value of the day starting from the beginning of the current month + * @property int $dayOfQuarter The value of the day starting from the beginning of the current quarter + * @property int $dayOfWeek 0 (for Sunday) through 6 (for Saturday) + * @property int $dayOfYear 1 through 366 + * @property int $decadeOfCentury The value of the decade starting from the beginning of the current century + * @property int $decadeOfMillennium The value of the decade starting from the beginning of the current millennium + * @property int $hourOfCentury The value of the hour starting from the beginning of the current century + * @property int $hourOfDay The value of the hour starting from the beginning of the current day + * @property int $hourOfDecade The value of the hour starting from the beginning of the current decade + * @property int $hourOfMillennium The value of the hour starting from the beginning of the current millennium + * @property int $hourOfMonth The value of the hour starting from the beginning of the current month + * @property int $hourOfQuarter The value of the hour starting from the beginning of the current quarter + * @property int $hourOfWeek The value of the hour starting from the beginning of the current week + * @property int $hourOfYear The value of the hour starting from the beginning of the current year + * @property int $microsecondOfCentury The value of the microsecond starting from the beginning of the current century + * @property int $microsecondOfDay The value of the microsecond starting from the beginning of the current day + * @property int $microsecondOfDecade The value of the microsecond starting from the beginning of the current decade + * @property int $microsecondOfHour The value of the microsecond starting from the beginning of the current hour + * @property int $microsecondOfMillennium The value of the microsecond starting from the beginning of the current millennium + * @property int $microsecondOfMillisecond The value of the microsecond starting from the beginning of the current millisecond + * @property int $microsecondOfMinute The value of the microsecond starting from the beginning of the current minute + * @property int $microsecondOfMonth The value of the microsecond starting from the beginning of the current month + * @property int $microsecondOfQuarter The value of the microsecond starting from the beginning of the current quarter + * @property int $microsecondOfSecond The value of the microsecond starting from the beginning of the current second + * @property int $microsecondOfWeek The value of the microsecond starting from the beginning of the current week + * @property int $microsecondOfYear The value of the microsecond starting from the beginning of the current year + * @property int $millisecondOfCentury The value of the millisecond starting from the beginning of the current century + * @property int $millisecondOfDay The value of the millisecond starting from the beginning of the current day + * @property int $millisecondOfDecade The value of the millisecond starting from the beginning of the current decade + * @property int $millisecondOfHour The value of the millisecond starting from the beginning of the current hour + * @property int $millisecondOfMillennium The value of the millisecond starting from the beginning of the current millennium + * @property int $millisecondOfMinute The value of the millisecond starting from the beginning of the current minute + * @property int $millisecondOfMonth The value of the millisecond starting from the beginning of the current month + * @property int $millisecondOfQuarter The value of the millisecond starting from the beginning of the current quarter + * @property int $millisecondOfSecond The value of the millisecond starting from the beginning of the current second + * @property int $millisecondOfWeek The value of the millisecond starting from the beginning of the current week + * @property int $millisecondOfYear The value of the millisecond starting from the beginning of the current year + * @property int $minuteOfCentury The value of the minute starting from the beginning of the current century + * @property int $minuteOfDay The value of the minute starting from the beginning of the current day + * @property int $minuteOfDecade The value of the minute starting from the beginning of the current decade + * @property int $minuteOfHour The value of the minute starting from the beginning of the current hour + * @property int $minuteOfMillennium The value of the minute starting from the beginning of the current millennium + * @property int $minuteOfMonth The value of the minute starting from the beginning of the current month + * @property int $minuteOfQuarter The value of the minute starting from the beginning of the current quarter + * @property int $minuteOfWeek The value of the minute starting from the beginning of the current week + * @property int $minuteOfYear The value of the minute starting from the beginning of the current year + * @property int $monthOfCentury The value of the month starting from the beginning of the current century + * @property int $monthOfDecade The value of the month starting from the beginning of the current decade + * @property int $monthOfMillennium The value of the month starting from the beginning of the current millennium + * @property int $monthOfQuarter The value of the month starting from the beginning of the current quarter + * @property int $monthOfYear The value of the month starting from the beginning of the current year + * @property int $quarterOfCentury The value of the quarter starting from the beginning of the current century + * @property int $quarterOfDecade The value of the quarter starting from the beginning of the current decade + * @property int $quarterOfMillennium The value of the quarter starting from the beginning of the current millennium + * @property int $quarterOfYear The value of the quarter starting from the beginning of the current year + * @property int $secondOfCentury The value of the second starting from the beginning of the current century + * @property int $secondOfDay The value of the second starting from the beginning of the current day + * @property int $secondOfDecade The value of the second starting from the beginning of the current decade + * @property int $secondOfHour The value of the second starting from the beginning of the current hour + * @property int $secondOfMillennium The value of the second starting from the beginning of the current millennium + * @property int $secondOfMinute The value of the second starting from the beginning of the current minute + * @property int $secondOfMonth The value of the second starting from the beginning of the current month + * @property int $secondOfQuarter The value of the second starting from the beginning of the current quarter + * @property int $secondOfWeek The value of the second starting from the beginning of the current week + * @property int $secondOfYear The value of the second starting from the beginning of the current year + * @property int $weekOfCentury The value of the week starting from the beginning of the current century + * @property int $weekOfDecade The value of the week starting from the beginning of the current decade + * @property int $weekOfMillennium The value of the week starting from the beginning of the current millennium + * @property int $weekOfMonth 1 through 5 + * @property int $weekOfQuarter The value of the week starting from the beginning of the current quarter + * @property int $weekOfYear ISO-8601 week number of year, weeks starting on Monday + * @property int $yearOfCentury The value of the year starting from the beginning of the current century + * @property int $yearOfDecade The value of the year starting from the beginning of the current decade + * @property int $yearOfMillennium The value of the year starting from the beginning of the current millennium * @property-read string $latinMeridiem "am"/"pm" (Ante meridiem or Post meridiem latin lowercase mark) * @property-read string $latinUpperMeridiem "AM"/"PM" (Ante meridiem or Post meridiem latin uppercase mark) * @property-read string $timezoneAbbreviatedName the current timezone abbreviated name @@ -80,13 +160,10 @@ * @property-read string $meridiem lowercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language * @property-read string $upperMeridiem uppercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language * @property-read int $noZeroHour current hour from 1 to 24 - * @property-read int $weeksInYear 51 through 53 * @property-read int $isoWeeksInYear 51 through 53 - * @property-read int $weekOfMonth 1 through 5 * @property-read int $weekNumberInMonth 1 through 5 * @property-read int $firstWeekDay 0 through 6 * @property-read int $lastWeekDay 0 through 6 - * @property-read int $daysInYear 365 or 366 * @property-read int $quarter the quarter of this instance, 1 - 4 * @property-read int $decade the decade of this instance * @property-read int $century the century of this instance @@ -97,6 +174,84 @@ * @property-read string $timezoneName the current timezone name * @property-read string $tzName alias of $timezoneName * @property-read string $locale locale of the current instance + * @property-read int $centuriesInMillennium The number of centuries contained in the current millennium + * @property-read int $daysInCentury The number of days contained in the current century + * @property-read int $daysInDecade The number of days contained in the current decade + * @property-read int $daysInMillennium The number of days contained in the current millennium + * @property-read int $daysInMonth number of days in the given month + * @property-read int $daysInQuarter The number of days contained in the current quarter + * @property-read int $daysInWeek The number of days contained in the current week + * @property-read int $daysInYear 365 or 366 + * @property-read int $decadesInCentury The number of decades contained in the current century + * @property-read int $decadesInMillennium The number of decades contained in the current millennium + * @property-read int $hoursInCentury The number of hours contained in the current century + * @property-read int $hoursInDay The number of hours contained in the current day + * @property-read int $hoursInDecade The number of hours contained in the current decade + * @property-read int $hoursInMillennium The number of hours contained in the current millennium + * @property-read int $hoursInMonth The number of hours contained in the current month + * @property-read int $hoursInQuarter The number of hours contained in the current quarter + * @property-read int $hoursInWeek The number of hours contained in the current week + * @property-read int $hoursInYear The number of hours contained in the current year + * @property-read int $microsecondsInCentury The number of microseconds contained in the current century + * @property-read int $microsecondsInDay The number of microseconds contained in the current day + * @property-read int $microsecondsInDecade The number of microseconds contained in the current decade + * @property-read int $microsecondsInHour The number of microseconds contained in the current hour + * @property-read int $microsecondsInMillennium The number of microseconds contained in the current millennium + * @property-read int $microsecondsInMillisecond The number of microseconds contained in the current millisecond + * @property-read int $microsecondsInMinute The number of microseconds contained in the current minute + * @property-read int $microsecondsInMonth The number of microseconds contained in the current month + * @property-read int $microsecondsInQuarter The number of microseconds contained in the current quarter + * @property-read int $microsecondsInSecond The number of microseconds contained in the current second + * @property-read int $microsecondsInWeek The number of microseconds contained in the current week + * @property-read int $microsecondsInYear The number of microseconds contained in the current year + * @property-read int $millisecondsInCentury The number of milliseconds contained in the current century + * @property-read int $millisecondsInDay The number of milliseconds contained in the current day + * @property-read int $millisecondsInDecade The number of milliseconds contained in the current decade + * @property-read int $millisecondsInHour The number of milliseconds contained in the current hour + * @property-read int $millisecondsInMillennium The number of milliseconds contained in the current millennium + * @property-read int $millisecondsInMinute The number of milliseconds contained in the current minute + * @property-read int $millisecondsInMonth The number of milliseconds contained in the current month + * @property-read int $millisecondsInQuarter The number of milliseconds contained in the current quarter + * @property-read int $millisecondsInSecond The number of milliseconds contained in the current second + * @property-read int $millisecondsInWeek The number of milliseconds contained in the current week + * @property-read int $millisecondsInYear The number of milliseconds contained in the current year + * @property-read int $minutesInCentury The number of minutes contained in the current century + * @property-read int $minutesInDay The number of minutes contained in the current day + * @property-read int $minutesInDecade The number of minutes contained in the current decade + * @property-read int $minutesInHour The number of minutes contained in the current hour + * @property-read int $minutesInMillennium The number of minutes contained in the current millennium + * @property-read int $minutesInMonth The number of minutes contained in the current month + * @property-read int $minutesInQuarter The number of minutes contained in the current quarter + * @property-read int $minutesInWeek The number of minutes contained in the current week + * @property-read int $minutesInYear The number of minutes contained in the current year + * @property-read int $monthsInCentury The number of months contained in the current century + * @property-read int $monthsInDecade The number of months contained in the current decade + * @property-read int $monthsInMillennium The number of months contained in the current millennium + * @property-read int $monthsInQuarter The number of months contained in the current quarter + * @property-read int $monthsInYear The number of months contained in the current year + * @property-read int $quartersInCentury The number of quarters contained in the current century + * @property-read int $quartersInDecade The number of quarters contained in the current decade + * @property-read int $quartersInMillennium The number of quarters contained in the current millennium + * @property-read int $quartersInYear The number of quarters contained in the current year + * @property-read int $secondsInCentury The number of seconds contained in the current century + * @property-read int $secondsInDay The number of seconds contained in the current day + * @property-read int $secondsInDecade The number of seconds contained in the current decade + * @property-read int $secondsInHour The number of seconds contained in the current hour + * @property-read int $secondsInMillennium The number of seconds contained in the current millennium + * @property-read int $secondsInMinute The number of seconds contained in the current minute + * @property-read int $secondsInMonth The number of seconds contained in the current month + * @property-read int $secondsInQuarter The number of seconds contained in the current quarter + * @property-read int $secondsInWeek The number of seconds contained in the current week + * @property-read int $secondsInYear The number of seconds contained in the current year + * @property-read int $weeksInCentury The number of weeks contained in the current century + * @property-read int $weeksInDecade The number of weeks contained in the current decade + * @property-read int $weeksInMillennium The number of weeks contained in the current millennium + * @property-read int $weeksInMonth The number of weeks contained in the current month + * @property-read int $weeksInQuarter The number of weeks contained in the current quarter + * @property-read int $weeksInYear 51 through 53 + * @property-read int $yearsInCentury The number of years contained in the current century + * @property-read int $yearsInDecade The number of years contained in the current decade + * @property-read int $yearsInMillennium The number of years contained in the current millennium * * @method bool isUtc() Check if the current instance has UTC timezone. (Both isUtc and isUTC cases are valid.) * @method bool isLocal() Check if the current instance has non-UTC timezone. @@ -109,64 +264,72 @@ * @method bool isThursday() Checks if the instance day is thursday. * @method bool isFriday() Checks if the instance day is friday. * @method bool isSaturday() Checks if the instance day is saturday. - * @method bool isSameYear(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same year as the instance. If null passed, compare to now (with the same timezone). + * @method bool isSameYear(DateTimeInterface|string $date) Checks if the given date is in the same year as the instance. If null passed, compare to now (with the same timezone). * @method bool isCurrentYear() Checks if the instance is in the same year as the current moment. * @method bool isNextYear() Checks if the instance is in the same year as the current moment next year. * @method bool isLastYear() Checks if the instance is in the same year as the current moment last year. - * @method bool isSameWeek(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same week as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMonth() Checks if the instance is in the same month as the current moment. + * @method bool isNextMonth() Checks if the instance is in the same month as the current moment next month. + * @method bool isLastMonth() Checks if the instance is in the same month as the current moment last month. + * @method bool isSameWeek(DateTimeInterface|string $date) Checks if the given date is in the same week as the instance. If null passed, compare to now (with the same timezone). * @method bool isCurrentWeek() Checks if the instance is in the same week as the current moment. * @method bool isNextWeek() Checks if the instance is in the same week as the current moment next week. * @method bool isLastWeek() Checks if the instance is in the same week as the current moment last week. - * @method bool isSameDay(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same day as the instance. If null passed, compare to now (with the same timezone). + * @method bool isSameDay(DateTimeInterface|string $date) Checks if the given date is in the same day as the instance. If null passed, compare to now (with the same timezone). * @method bool isCurrentDay() Checks if the instance is in the same day as the current moment. * @method bool isNextDay() Checks if the instance is in the same day as the current moment next day. * @method bool isLastDay() Checks if the instance is in the same day as the current moment last day. - * @method bool isSameHour(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same hour as the instance. If null passed, compare to now (with the same timezone). + * @method bool isSameHour(DateTimeInterface|string $date) Checks if the given date is in the same hour as the instance. If null passed, compare to now (with the same timezone). * @method bool isCurrentHour() Checks if the instance is in the same hour as the current moment. * @method bool isNextHour() Checks if the instance is in the same hour as the current moment next hour. * @method bool isLastHour() Checks if the instance is in the same hour as the current moment last hour. - * @method bool isSameMinute(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same minute as the instance. If null passed, compare to now (with the same timezone). + * @method bool isSameMinute(DateTimeInterface|string $date) Checks if the given date is in the same minute as the instance. If null passed, compare to now (with the same timezone). * @method bool isCurrentMinute() Checks if the instance is in the same minute as the current moment. * @method bool isNextMinute() Checks if the instance is in the same minute as the current moment next minute. * @method bool isLastMinute() Checks if the instance is in the same minute as the current moment last minute. - * @method bool isSameSecond(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same second as the instance. If null passed, compare to now (with the same timezone). + * @method bool isSameSecond(DateTimeInterface|string $date) Checks if the given date is in the same second as the instance. If null passed, compare to now (with the same timezone). * @method bool isCurrentSecond() Checks if the instance is in the same second as the current moment. * @method bool isNextSecond() Checks if the instance is in the same second as the current moment next second. * @method bool isLastSecond() Checks if the instance is in the same second as the current moment last second. - * @method bool isSameMicro(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). + * @method bool isSameMilli(DateTimeInterface|string $date) Checks if the given date is in the same millisecond as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMilli() Checks if the instance is in the same millisecond as the current moment. + * @method bool isNextMilli() Checks if the instance is in the same millisecond as the current moment next millisecond. + * @method bool isLastMilli() Checks if the instance is in the same millisecond as the current moment last millisecond. + * @method bool isSameMillisecond(DateTimeInterface|string $date) Checks if the given date is in the same millisecond as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMillisecond() Checks if the instance is in the same millisecond as the current moment. + * @method bool isNextMillisecond() Checks if the instance is in the same millisecond as the current moment next millisecond. + * @method bool isLastMillisecond() Checks if the instance is in the same millisecond as the current moment last millisecond. + * @method bool isSameMicro(DateTimeInterface|string $date) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). * @method bool isCurrentMicro() Checks if the instance is in the same microsecond as the current moment. * @method bool isNextMicro() Checks if the instance is in the same microsecond as the current moment next microsecond. * @method bool isLastMicro() Checks if the instance is in the same microsecond as the current moment last microsecond. - * @method bool isSameMicrosecond(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). + * @method bool isSameMicrosecond(DateTimeInterface|string $date) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). * @method bool isCurrentMicrosecond() Checks if the instance is in the same microsecond as the current moment. * @method bool isNextMicrosecond() Checks if the instance is in the same microsecond as the current moment next microsecond. * @method bool isLastMicrosecond() Checks if the instance is in the same microsecond as the current moment last microsecond. - * @method bool isCurrentMonth() Checks if the instance is in the same month as the current moment. - * @method bool isNextMonth() Checks if the instance is in the same month as the current moment next month. - * @method bool isLastMonth() Checks if the instance is in the same month as the current moment last month. - * @method bool isCurrentQuarter() Checks if the instance is in the same quarter as the current moment. - * @method bool isNextQuarter() Checks if the instance is in the same quarter as the current moment next quarter. - * @method bool isLastQuarter() Checks if the instance is in the same quarter as the current moment last quarter. - * @method bool isSameDecade(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same decade as the instance. If null passed, compare to now (with the same timezone). + * @method bool isSameDecade(DateTimeInterface|string $date) Checks if the given date is in the same decade as the instance. If null passed, compare to now (with the same timezone). * @method bool isCurrentDecade() Checks if the instance is in the same decade as the current moment. * @method bool isNextDecade() Checks if the instance is in the same decade as the current moment next decade. * @method bool isLastDecade() Checks if the instance is in the same decade as the current moment last decade. - * @method bool isSameCentury(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same century as the instance. If null passed, compare to now (with the same timezone). + * @method bool isSameCentury(DateTimeInterface|string $date) Checks if the given date is in the same century as the instance. If null passed, compare to now (with the same timezone). * @method bool isCurrentCentury() Checks if the instance is in the same century as the current moment. * @method bool isNextCentury() Checks if the instance is in the same century as the current moment next century. * @method bool isLastCentury() Checks if the instance is in the same century as the current moment last century. - * @method bool isSameMillennium(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same millennium as the instance. If null passed, compare to now (with the same timezone). + * @method bool isSameMillennium(DateTimeInterface|string $date) Checks if the given date is in the same millennium as the instance. If null passed, compare to now (with the same timezone). * @method bool isCurrentMillennium() Checks if the instance is in the same millennium as the current moment. * @method bool isNextMillennium() Checks if the instance is in the same millennium as the current moment next millennium. * @method bool isLastMillennium() Checks if the instance is in the same millennium as the current moment last millennium. + * @method bool isCurrentQuarter() Checks if the instance is in the same quarter as the current moment. + * @method bool isNextQuarter() Checks if the instance is in the same quarter as the current moment next quarter. + * @method bool isLastQuarter() Checks if the instance is in the same quarter as the current moment last quarter. * @method CarbonInterface years(int $value) Set current instance year to the given value. * @method CarbonInterface year(int $value) Set current instance year to the given value. * @method CarbonInterface setYears(int $value) Set current instance year to the given value. * @method CarbonInterface setYear(int $value) Set current instance year to the given value. - * @method CarbonInterface months(int $value) Set current instance month to the given value. - * @method CarbonInterface month(int $value) Set current instance month to the given value. - * @method CarbonInterface setMonths(int $value) Set current instance month to the given value. - * @method CarbonInterface setMonth(int $value) Set current instance month to the given value. + * @method CarbonInterface months(Month|int $value) Set current instance month to the given value. + * @method CarbonInterface month(Month|int $value) Set current instance month to the given value. + * @method CarbonInterface setMonths(Month|int $value) Set current instance month to the given value. + * @method CarbonInterface setMonth(Month|int $value) Set current instance month to the given value. * @method CarbonInterface days(int $value) Set current instance day to the given value. * @method CarbonInterface day(int $value) Set current instance day to the given value. * @method CarbonInterface setDays(int $value) Set current instance day to the given value. @@ -199,241 +362,256 @@ * @method CarbonInterface microsecond(int $value) Set current instance microsecond to the given value. * @method CarbonInterface setMicroseconds(int $value) Set current instance microsecond to the given value. * @method CarbonInterface setMicrosecond(int $value) Set current instance microsecond to the given value. - * @method CarbonInterface addYears(int $value = 1) Add years (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addYears(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addYear() Add one year to the instance (using date interval). - * @method CarbonInterface subYears(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subYears(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subYear() Sub one year to the instance (using date interval). - * @method CarbonInterface addYearsWithOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addYearsWithOverflow(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. * @method CarbonInterface addYearWithOverflow() Add one year to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonInterface subYearsWithOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subYearsWithOverflow(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. * @method CarbonInterface subYearWithOverflow() Sub one year to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonInterface addYearsWithoutOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addYearsWithoutOverflow(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addYearWithoutOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subYearsWithoutOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subYearsWithoutOverflow(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subYearWithoutOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addYearsWithNoOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addYearsWithNoOverflow(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addYearWithNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subYearsWithNoOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subYearsWithNoOverflow(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subYearWithNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addYearsNoOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addYearsNoOverflow(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addYearNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subYearsNoOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subYearsNoOverflow(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subYearNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addMonths(int $value = 1) Add months (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMonths(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addMonth() Add one month to the instance (using date interval). - * @method CarbonInterface subMonths(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMonths(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subMonth() Sub one month to the instance (using date interval). - * @method CarbonInterface addMonthsWithOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addMonthsWithOverflow(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. * @method CarbonInterface addMonthWithOverflow() Add one month to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonInterface subMonthsWithOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subMonthsWithOverflow(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. * @method CarbonInterface subMonthWithOverflow() Sub one month to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonInterface addMonthsWithoutOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMonthsWithoutOverflow(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addMonthWithoutOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subMonthsWithoutOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMonthsWithoutOverflow(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subMonthWithoutOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addMonthsWithNoOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMonthsWithNoOverflow(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addMonthWithNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subMonthsWithNoOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMonthsWithNoOverflow(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subMonthWithNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addMonthsNoOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMonthsNoOverflow(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addMonthNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subMonthsNoOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMonthsNoOverflow(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subMonthNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addDays(int $value = 1) Add days (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addDays(int|float $value = 1) Add days (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addDay() Add one day to the instance (using date interval). - * @method CarbonInterface subDays(int $value = 1) Sub days (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subDays(int|float $value = 1) Sub days (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subDay() Sub one day to the instance (using date interval). - * @method CarbonInterface addHours(int $value = 1) Add hours (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addHours(int|float $value = 1) Add hours (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addHour() Add one hour to the instance (using date interval). - * @method CarbonInterface subHours(int $value = 1) Sub hours (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subHours(int|float $value = 1) Sub hours (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subHour() Sub one hour to the instance (using date interval). - * @method CarbonInterface addMinutes(int $value = 1) Add minutes (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMinutes(int|float $value = 1) Add minutes (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addMinute() Add one minute to the instance (using date interval). - * @method CarbonInterface subMinutes(int $value = 1) Sub minutes (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMinutes(int|float $value = 1) Sub minutes (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subMinute() Sub one minute to the instance (using date interval). - * @method CarbonInterface addSeconds(int $value = 1) Add seconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addSeconds(int|float $value = 1) Add seconds (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addSecond() Add one second to the instance (using date interval). - * @method CarbonInterface subSeconds(int $value = 1) Sub seconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subSeconds(int|float $value = 1) Sub seconds (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subSecond() Sub one second to the instance (using date interval). - * @method CarbonInterface addMillis(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMillis(int|float $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addMilli() Add one millisecond to the instance (using date interval). - * @method CarbonInterface subMillis(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMillis(int|float $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subMilli() Sub one millisecond to the instance (using date interval). - * @method CarbonInterface addMilliseconds(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMilliseconds(int|float $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addMillisecond() Add one millisecond to the instance (using date interval). - * @method CarbonInterface subMilliseconds(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMilliseconds(int|float $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subMillisecond() Sub one millisecond to the instance (using date interval). - * @method CarbonInterface addMicros(int $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMicros(int|float $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addMicro() Add one microsecond to the instance (using date interval). - * @method CarbonInterface subMicros(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMicros(int|float $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subMicro() Sub one microsecond to the instance (using date interval). - * @method CarbonInterface addMicroseconds(int $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMicroseconds(int|float $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addMicrosecond() Add one microsecond to the instance (using date interval). - * @method CarbonInterface subMicroseconds(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMicroseconds(int|float $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subMicrosecond() Sub one microsecond to the instance (using date interval). - * @method CarbonInterface addMillennia(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMillennia(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addMillennium() Add one millennium to the instance (using date interval). - * @method CarbonInterface subMillennia(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMillennia(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subMillennium() Sub one millennium to the instance (using date interval). - * @method CarbonInterface addMillenniaWithOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addMillenniaWithOverflow(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. * @method CarbonInterface addMillenniumWithOverflow() Add one millennium to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonInterface subMillenniaWithOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subMillenniaWithOverflow(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. * @method CarbonInterface subMillenniumWithOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonInterface addMillenniaWithoutOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMillenniaWithoutOverflow(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addMillenniumWithoutOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subMillenniaWithoutOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMillenniaWithoutOverflow(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subMillenniumWithoutOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addMillenniaWithNoOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMillenniaWithNoOverflow(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addMillenniumWithNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subMillenniaWithNoOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMillenniaWithNoOverflow(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subMillenniumWithNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addMillenniaNoOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMillenniaNoOverflow(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addMillenniumNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subMillenniaNoOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMillenniaNoOverflow(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subMillenniumNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addCenturies(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addCenturies(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addCentury() Add one century to the instance (using date interval). - * @method CarbonInterface subCenturies(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subCenturies(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subCentury() Sub one century to the instance (using date interval). - * @method CarbonInterface addCenturiesWithOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addCenturiesWithOverflow(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. * @method CarbonInterface addCenturyWithOverflow() Add one century to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonInterface subCenturiesWithOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subCenturiesWithOverflow(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. * @method CarbonInterface subCenturyWithOverflow() Sub one century to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonInterface addCenturiesWithoutOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addCenturiesWithoutOverflow(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addCenturyWithoutOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subCenturiesWithoutOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subCenturiesWithoutOverflow(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subCenturyWithoutOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addCenturiesWithNoOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addCenturiesWithNoOverflow(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addCenturyWithNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subCenturiesWithNoOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subCenturiesWithNoOverflow(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subCenturyWithNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addCenturiesNoOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addCenturiesNoOverflow(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addCenturyNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subCenturiesNoOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subCenturiesNoOverflow(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subCenturyNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addDecades(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addDecades(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addDecade() Add one decade to the instance (using date interval). - * @method CarbonInterface subDecades(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subDecades(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subDecade() Sub one decade to the instance (using date interval). - * @method CarbonInterface addDecadesWithOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addDecadesWithOverflow(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. * @method CarbonInterface addDecadeWithOverflow() Add one decade to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonInterface subDecadesWithOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subDecadesWithOverflow(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. * @method CarbonInterface subDecadeWithOverflow() Sub one decade to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonInterface addDecadesWithoutOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addDecadesWithoutOverflow(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addDecadeWithoutOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subDecadesWithoutOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subDecadesWithoutOverflow(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subDecadeWithoutOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addDecadesWithNoOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addDecadesWithNoOverflow(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addDecadeWithNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subDecadesWithNoOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subDecadesWithNoOverflow(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subDecadeWithNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addDecadesNoOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addDecadesNoOverflow(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addDecadeNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subDecadesNoOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subDecadesNoOverflow(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subDecadeNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addQuarters(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addQuarters(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addQuarter() Add one quarter to the instance (using date interval). - * @method CarbonInterface subQuarters(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subQuarters(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subQuarter() Sub one quarter to the instance (using date interval). - * @method CarbonInterface addQuartersWithOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addQuartersWithOverflow(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. * @method CarbonInterface addQuarterWithOverflow() Add one quarter to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonInterface subQuartersWithOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subQuartersWithOverflow(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. * @method CarbonInterface subQuarterWithOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonInterface addQuartersWithoutOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addQuartersWithoutOverflow(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addQuarterWithoutOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subQuartersWithoutOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subQuartersWithoutOverflow(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subQuarterWithoutOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addQuartersWithNoOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addQuartersWithNoOverflow(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addQuarterWithNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subQuartersWithNoOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subQuartersWithNoOverflow(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subQuarterWithNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addQuartersNoOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addQuartersNoOverflow(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addQuarterNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subQuartersNoOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subQuartersNoOverflow(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subQuarterNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addWeeks(int $value = 1) Add weeks (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addWeeks(int|float $value = 1) Add weeks (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addWeek() Add one week to the instance (using date interval). - * @method CarbonInterface subWeeks(int $value = 1) Sub weeks (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subWeeks(int|float $value = 1) Sub weeks (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subWeek() Sub one week to the instance (using date interval). - * @method CarbonInterface addWeekdays(int $value = 1) Add weekdays (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addWeekdays(int|float $value = 1) Add weekdays (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addWeekday() Add one weekday to the instance (using date interval). - * @method CarbonInterface subWeekdays(int $value = 1) Sub weekdays (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subWeekdays(int|float $value = 1) Sub weekdays (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subWeekday() Sub one weekday to the instance (using date interval). - * @method CarbonInterface addRealMicros(int $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealMicro() Add one microsecond to the instance (using timestamp). - * @method CarbonInterface subRealMicros(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealMicro() Sub one microsecond to the instance (using timestamp). - * @method CarbonPeriod microsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. - * @method CarbonInterface addRealMicroseconds(int $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealMicrosecond() Add one microsecond to the instance (using timestamp). - * @method CarbonInterface subRealMicroseconds(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealMicrosecond() Sub one microsecond to the instance (using timestamp). - * @method CarbonPeriod microsecondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. - * @method CarbonInterface addRealMillis(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealMilli() Add one millisecond to the instance (using timestamp). - * @method CarbonInterface subRealMillis(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealMilli() Sub one millisecond to the instance (using timestamp). - * @method CarbonPeriod millisUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. - * @method CarbonInterface addRealMilliseconds(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealMillisecond() Add one millisecond to the instance (using timestamp). - * @method CarbonInterface subRealMilliseconds(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealMillisecond() Sub one millisecond to the instance (using timestamp). - * @method CarbonPeriod millisecondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. - * @method CarbonInterface addRealSeconds(int $value = 1) Add seconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealSecond() Add one second to the instance (using timestamp). - * @method CarbonInterface subRealSeconds(int $value = 1) Sub seconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealSecond() Sub one second to the instance (using timestamp). - * @method CarbonPeriod secondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each second or every X seconds if a factor is given. - * @method CarbonInterface addRealMinutes(int $value = 1) Add minutes (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealMinute() Add one minute to the instance (using timestamp). - * @method CarbonInterface subRealMinutes(int $value = 1) Sub minutes (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealMinute() Sub one minute to the instance (using timestamp). - * @method CarbonPeriod minutesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each minute or every X minutes if a factor is given. - * @method CarbonInterface addRealHours(int $value = 1) Add hours (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealHour() Add one hour to the instance (using timestamp). - * @method CarbonInterface subRealHours(int $value = 1) Sub hours (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealHour() Sub one hour to the instance (using timestamp). - * @method CarbonPeriod hoursUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each hour or every X hours if a factor is given. - * @method CarbonInterface addRealDays(int $value = 1) Add days (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealDay() Add one day to the instance (using timestamp). - * @method CarbonInterface subRealDays(int $value = 1) Sub days (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealDay() Sub one day to the instance (using timestamp). - * @method CarbonPeriod daysUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each day or every X days if a factor is given. - * @method CarbonInterface addRealWeeks(int $value = 1) Add weeks (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealWeek() Add one week to the instance (using timestamp). - * @method CarbonInterface subRealWeeks(int $value = 1) Sub weeks (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealWeek() Sub one week to the instance (using timestamp). - * @method CarbonPeriod weeksUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each week or every X weeks if a factor is given. - * @method CarbonInterface addRealMonths(int $value = 1) Add months (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealMonth() Add one month to the instance (using timestamp). - * @method CarbonInterface subRealMonths(int $value = 1) Sub months (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealMonth() Sub one month to the instance (using timestamp). - * @method CarbonPeriod monthsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each month or every X months if a factor is given. - * @method CarbonInterface addRealQuarters(int $value = 1) Add quarters (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealQuarter() Add one quarter to the instance (using timestamp). - * @method CarbonInterface subRealQuarters(int $value = 1) Sub quarters (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealQuarter() Sub one quarter to the instance (using timestamp). - * @method CarbonPeriod quartersUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each quarter or every X quarters if a factor is given. - * @method CarbonInterface addRealYears(int $value = 1) Add years (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealYear() Add one year to the instance (using timestamp). - * @method CarbonInterface subRealYears(int $value = 1) Sub years (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealYear() Sub one year to the instance (using timestamp). - * @method CarbonPeriod yearsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each year or every X years if a factor is given. - * @method CarbonInterface addRealDecades(int $value = 1) Add decades (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealDecade() Add one decade to the instance (using timestamp). - * @method CarbonInterface subRealDecades(int $value = 1) Sub decades (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealDecade() Sub one decade to the instance (using timestamp). - * @method CarbonPeriod decadesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each decade or every X decades if a factor is given. - * @method CarbonInterface addRealCenturies(int $value = 1) Add centuries (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealCentury() Add one century to the instance (using timestamp). - * @method CarbonInterface subRealCenturies(int $value = 1) Sub centuries (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealCentury() Sub one century to the instance (using timestamp). - * @method CarbonPeriod centuriesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each century or every X centuries if a factor is given. - * @method CarbonInterface addRealMillennia(int $value = 1) Add millennia (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealMillennium() Add one millennium to the instance (using timestamp). - * @method CarbonInterface subRealMillennia(int $value = 1) Sub millennia (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealMillennium() Sub one millennium to the instance (using timestamp). - * @method CarbonPeriod millenniaUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millennium or every X millennia if a factor is given. + * @method CarbonInterface addUTCMicros(int|float $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCMicro() Add one microsecond to the instance (using timestamp). + * @method CarbonInterface subUTCMicros(int|float $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCMicro() Sub one microsecond to the instance (using timestamp). + * @method CarbonPeriod microsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. + * @method float diffInUTCMicros(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of microseconds. + * @method CarbonInterface addUTCMicroseconds(int|float $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCMicrosecond() Add one microsecond to the instance (using timestamp). + * @method CarbonInterface subUTCMicroseconds(int|float $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCMicrosecond() Sub one microsecond to the instance (using timestamp). + * @method CarbonPeriod microsecondsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. + * @method float diffInUTCMicroseconds(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of microseconds. + * @method CarbonInterface addUTCMillis(int|float $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCMilli() Add one millisecond to the instance (using timestamp). + * @method CarbonInterface subUTCMillis(int|float $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCMilli() Sub one millisecond to the instance (using timestamp). + * @method CarbonPeriod millisUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. + * @method float diffInUTCMillis(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of milliseconds. + * @method CarbonInterface addUTCMilliseconds(int|float $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCMillisecond() Add one millisecond to the instance (using timestamp). + * @method CarbonInterface subUTCMilliseconds(int|float $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCMillisecond() Sub one millisecond to the instance (using timestamp). + * @method CarbonPeriod millisecondsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. + * @method float diffInUTCMilliseconds(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of milliseconds. + * @method CarbonInterface addUTCSeconds(int|float $value = 1) Add seconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCSecond() Add one second to the instance (using timestamp). + * @method CarbonInterface subUTCSeconds(int|float $value = 1) Sub seconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCSecond() Sub one second to the instance (using timestamp). + * @method CarbonPeriod secondsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each second or every X seconds if a factor is given. + * @method float diffInUTCSeconds(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of seconds. + * @method CarbonInterface addUTCMinutes(int|float $value = 1) Add minutes (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCMinute() Add one minute to the instance (using timestamp). + * @method CarbonInterface subUTCMinutes(int|float $value = 1) Sub minutes (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCMinute() Sub one minute to the instance (using timestamp). + * @method CarbonPeriod minutesUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each minute or every X minutes if a factor is given. + * @method float diffInUTCMinutes(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of minutes. + * @method CarbonInterface addUTCHours(int|float $value = 1) Add hours (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCHour() Add one hour to the instance (using timestamp). + * @method CarbonInterface subUTCHours(int|float $value = 1) Sub hours (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCHour() Sub one hour to the instance (using timestamp). + * @method CarbonPeriod hoursUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each hour or every X hours if a factor is given. + * @method float diffInUTCHours(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of hours. + * @method CarbonInterface addUTCDays(int|float $value = 1) Add days (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCDay() Add one day to the instance (using timestamp). + * @method CarbonInterface subUTCDays(int|float $value = 1) Sub days (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCDay() Sub one day to the instance (using timestamp). + * @method CarbonPeriod daysUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each day or every X days if a factor is given. + * @method float diffInUTCDays(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of days. + * @method CarbonInterface addUTCWeeks(int|float $value = 1) Add weeks (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCWeek() Add one week to the instance (using timestamp). + * @method CarbonInterface subUTCWeeks(int|float $value = 1) Sub weeks (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCWeek() Sub one week to the instance (using timestamp). + * @method CarbonPeriod weeksUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each week or every X weeks if a factor is given. + * @method float diffInUTCWeeks(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of weeks. + * @method CarbonInterface addUTCMonths(int|float $value = 1) Add months (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCMonth() Add one month to the instance (using timestamp). + * @method CarbonInterface subUTCMonths(int|float $value = 1) Sub months (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCMonth() Sub one month to the instance (using timestamp). + * @method CarbonPeriod monthsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each month or every X months if a factor is given. + * @method float diffInUTCMonths(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of months. + * @method CarbonInterface addUTCQuarters(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCQuarter() Add one quarter to the instance (using timestamp). + * @method CarbonInterface subUTCQuarters(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCQuarter() Sub one quarter to the instance (using timestamp). + * @method CarbonPeriod quartersUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each quarter or every X quarters if a factor is given. + * @method float diffInUTCQuarters(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of quarters. + * @method CarbonInterface addUTCYears(int|float $value = 1) Add years (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCYear() Add one year to the instance (using timestamp). + * @method CarbonInterface subUTCYears(int|float $value = 1) Sub years (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCYear() Sub one year to the instance (using timestamp). + * @method CarbonPeriod yearsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each year or every X years if a factor is given. + * @method float diffInUTCYears(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of years. + * @method CarbonInterface addUTCDecades(int|float $value = 1) Add decades (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCDecade() Add one decade to the instance (using timestamp). + * @method CarbonInterface subUTCDecades(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCDecade() Sub one decade to the instance (using timestamp). + * @method CarbonPeriod decadesUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each decade or every X decades if a factor is given. + * @method float diffInUTCDecades(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of decades. + * @method CarbonInterface addUTCCenturies(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCCentury() Add one century to the instance (using timestamp). + * @method CarbonInterface subUTCCenturies(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCCentury() Sub one century to the instance (using timestamp). + * @method CarbonPeriod centuriesUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each century or every X centuries if a factor is given. + * @method float diffInUTCCenturies(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of centuries. + * @method CarbonInterface addUTCMillennia(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCMillennium() Add one millennium to the instance (using timestamp). + * @method CarbonInterface subUTCMillennia(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCMillennium() Sub one millennium to the instance (using timestamp). + * @method CarbonPeriod millenniaUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millennium or every X millennia if a factor is given. + * @method float diffInUTCMillennia(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of millennia. * @method CarbonInterface roundYear(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. * @method CarbonInterface roundYears(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. * @method CarbonInterface floorYear(float $precision = 1) Truncate the current instance year with given precision. @@ -514,8 +692,164 @@ * @method string longRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) * @method string shortRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) * @method string longRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method int centuriesInMillennium() Return the number of centuries contained in the current millennium + * @method int|static centuryOfMillennium(?int $century = null) Return the value of the century starting from the beginning of the current millennium when called with no parameters, change the current century when called with an integer value + * @method int|static dayOfCentury(?int $day = null) Return the value of the day starting from the beginning of the current century when called with no parameters, change the current day when called with an integer value + * @method int|static dayOfDecade(?int $day = null) Return the value of the day starting from the beginning of the current decade when called with no parameters, change the current day when called with an integer value + * @method int|static dayOfMillennium(?int $day = null) Return the value of the day starting from the beginning of the current millennium when called with no parameters, change the current day when called with an integer value + * @method int|static dayOfMonth(?int $day = null) Return the value of the day starting from the beginning of the current month when called with no parameters, change the current day when called with an integer value + * @method int|static dayOfQuarter(?int $day = null) Return the value of the day starting from the beginning of the current quarter when called with no parameters, change the current day when called with an integer value + * @method int|static dayOfWeek(?int $day = null) Return the value of the day starting from the beginning of the current week when called with no parameters, change the current day when called with an integer value + * @method int daysInCentury() Return the number of days contained in the current century + * @method int daysInDecade() Return the number of days contained in the current decade + * @method int daysInMillennium() Return the number of days contained in the current millennium + * @method int daysInMonth() Return the number of days contained in the current month + * @method int daysInQuarter() Return the number of days contained in the current quarter + * @method int daysInWeek() Return the number of days contained in the current week + * @method int daysInYear() Return the number of days contained in the current year + * @method int|static decadeOfCentury(?int $decade = null) Return the value of the decade starting from the beginning of the current century when called with no parameters, change the current decade when called with an integer value + * @method int|static decadeOfMillennium(?int $decade = null) Return the value of the decade starting from the beginning of the current millennium when called with no parameters, change the current decade when called with an integer value + * @method int decadesInCentury() Return the number of decades contained in the current century + * @method int decadesInMillennium() Return the number of decades contained in the current millennium + * @method int|static hourOfCentury(?int $hour = null) Return the value of the hour starting from the beginning of the current century when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfDay(?int $hour = null) Return the value of the hour starting from the beginning of the current day when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfDecade(?int $hour = null) Return the value of the hour starting from the beginning of the current decade when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfMillennium(?int $hour = null) Return the value of the hour starting from the beginning of the current millennium when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfMonth(?int $hour = null) Return the value of the hour starting from the beginning of the current month when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfQuarter(?int $hour = null) Return the value of the hour starting from the beginning of the current quarter when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfWeek(?int $hour = null) Return the value of the hour starting from the beginning of the current week when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfYear(?int $hour = null) Return the value of the hour starting from the beginning of the current year when called with no parameters, change the current hour when called with an integer value + * @method int hoursInCentury() Return the number of hours contained in the current century + * @method int hoursInDay() Return the number of hours contained in the current day + * @method int hoursInDecade() Return the number of hours contained in the current decade + * @method int hoursInMillennium() Return the number of hours contained in the current millennium + * @method int hoursInMonth() Return the number of hours contained in the current month + * @method int hoursInQuarter() Return the number of hours contained in the current quarter + * @method int hoursInWeek() Return the number of hours contained in the current week + * @method int hoursInYear() Return the number of hours contained in the current year + * @method int|static microsecondOfCentury(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current century when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfDay(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current day when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfDecade(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current decade when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfHour(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current hour when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfMillennium(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current millennium when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfMillisecond(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current millisecond when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfMinute(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current minute when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfMonth(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current month when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfQuarter(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current quarter when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfSecond(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current second when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfWeek(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current week when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfYear(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current year when called with no parameters, change the current microsecond when called with an integer value + * @method int microsecondsInCentury() Return the number of microseconds contained in the current century + * @method int microsecondsInDay() Return the number of microseconds contained in the current day + * @method int microsecondsInDecade() Return the number of microseconds contained in the current decade + * @method int microsecondsInHour() Return the number of microseconds contained in the current hour + * @method int microsecondsInMillennium() Return the number of microseconds contained in the current millennium + * @method int microsecondsInMillisecond() Return the number of microseconds contained in the current millisecond + * @method int microsecondsInMinute() Return the number of microseconds contained in the current minute + * @method int microsecondsInMonth() Return the number of microseconds contained in the current month + * @method int microsecondsInQuarter() Return the number of microseconds contained in the current quarter + * @method int microsecondsInSecond() Return the number of microseconds contained in the current second + * @method int microsecondsInWeek() Return the number of microseconds contained in the current week + * @method int microsecondsInYear() Return the number of microseconds contained in the current year + * @method int|static millisecondOfCentury(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current century when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfDay(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current day when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfDecade(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current decade when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfHour(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current hour when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfMillennium(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current millennium when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfMinute(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current minute when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfMonth(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current month when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfQuarter(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current quarter when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfSecond(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current second when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfWeek(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current week when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfYear(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current year when called with no parameters, change the current millisecond when called with an integer value + * @method int millisecondsInCentury() Return the number of milliseconds contained in the current century + * @method int millisecondsInDay() Return the number of milliseconds contained in the current day + * @method int millisecondsInDecade() Return the number of milliseconds contained in the current decade + * @method int millisecondsInHour() Return the number of milliseconds contained in the current hour + * @method int millisecondsInMillennium() Return the number of milliseconds contained in the current millennium + * @method int millisecondsInMinute() Return the number of milliseconds contained in the current minute + * @method int millisecondsInMonth() Return the number of milliseconds contained in the current month + * @method int millisecondsInQuarter() Return the number of milliseconds contained in the current quarter + * @method int millisecondsInSecond() Return the number of milliseconds contained in the current second + * @method int millisecondsInWeek() Return the number of milliseconds contained in the current week + * @method int millisecondsInYear() Return the number of milliseconds contained in the current year + * @method int|static minuteOfCentury(?int $minute = null) Return the value of the minute starting from the beginning of the current century when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfDay(?int $minute = null) Return the value of the minute starting from the beginning of the current day when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfDecade(?int $minute = null) Return the value of the minute starting from the beginning of the current decade when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfHour(?int $minute = null) Return the value of the minute starting from the beginning of the current hour when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfMillennium(?int $minute = null) Return the value of the minute starting from the beginning of the current millennium when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfMonth(?int $minute = null) Return the value of the minute starting from the beginning of the current month when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfQuarter(?int $minute = null) Return the value of the minute starting from the beginning of the current quarter when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfWeek(?int $minute = null) Return the value of the minute starting from the beginning of the current week when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfYear(?int $minute = null) Return the value of the minute starting from the beginning of the current year when called with no parameters, change the current minute when called with an integer value + * @method int minutesInCentury() Return the number of minutes contained in the current century + * @method int minutesInDay() Return the number of minutes contained in the current day + * @method int minutesInDecade() Return the number of minutes contained in the current decade + * @method int minutesInHour() Return the number of minutes contained in the current hour + * @method int minutesInMillennium() Return the number of minutes contained in the current millennium + * @method int minutesInMonth() Return the number of minutes contained in the current month + * @method int minutesInQuarter() Return the number of minutes contained in the current quarter + * @method int minutesInWeek() Return the number of minutes contained in the current week + * @method int minutesInYear() Return the number of minutes contained in the current year + * @method int|static monthOfCentury(?int $month = null) Return the value of the month starting from the beginning of the current century when called with no parameters, change the current month when called with an integer value + * @method int|static monthOfDecade(?int $month = null) Return the value of the month starting from the beginning of the current decade when called with no parameters, change the current month when called with an integer value + * @method int|static monthOfMillennium(?int $month = null) Return the value of the month starting from the beginning of the current millennium when called with no parameters, change the current month when called with an integer value + * @method int|static monthOfQuarter(?int $month = null) Return the value of the month starting from the beginning of the current quarter when called with no parameters, change the current month when called with an integer value + * @method int|static monthOfYear(?int $month = null) Return the value of the month starting from the beginning of the current year when called with no parameters, change the current month when called with an integer value + * @method int monthsInCentury() Return the number of months contained in the current century + * @method int monthsInDecade() Return the number of months contained in the current decade + * @method int monthsInMillennium() Return the number of months contained in the current millennium + * @method int monthsInQuarter() Return the number of months contained in the current quarter + * @method int monthsInYear() Return the number of months contained in the current year + * @method int|static quarterOfCentury(?int $quarter = null) Return the value of the quarter starting from the beginning of the current century when called with no parameters, change the current quarter when called with an integer value + * @method int|static quarterOfDecade(?int $quarter = null) Return the value of the quarter starting from the beginning of the current decade when called with no parameters, change the current quarter when called with an integer value + * @method int|static quarterOfMillennium(?int $quarter = null) Return the value of the quarter starting from the beginning of the current millennium when called with no parameters, change the current quarter when called with an integer value + * @method int|static quarterOfYear(?int $quarter = null) Return the value of the quarter starting from the beginning of the current year when called with no parameters, change the current quarter when called with an integer value + * @method int quartersInCentury() Return the number of quarters contained in the current century + * @method int quartersInDecade() Return the number of quarters contained in the current decade + * @method int quartersInMillennium() Return the number of quarters contained in the current millennium + * @method int quartersInYear() Return the number of quarters contained in the current year + * @method int|static secondOfCentury(?int $second = null) Return the value of the second starting from the beginning of the current century when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfDay(?int $second = null) Return the value of the second starting from the beginning of the current day when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfDecade(?int $second = null) Return the value of the second starting from the beginning of the current decade when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfHour(?int $second = null) Return the value of the second starting from the beginning of the current hour when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfMillennium(?int $second = null) Return the value of the second starting from the beginning of the current millennium when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfMinute(?int $second = null) Return the value of the second starting from the beginning of the current minute when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfMonth(?int $second = null) Return the value of the second starting from the beginning of the current month when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfQuarter(?int $second = null) Return the value of the second starting from the beginning of the current quarter when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfWeek(?int $second = null) Return the value of the second starting from the beginning of the current week when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfYear(?int $second = null) Return the value of the second starting from the beginning of the current year when called with no parameters, change the current second when called with an integer value + * @method int secondsInCentury() Return the number of seconds contained in the current century + * @method int secondsInDay() Return the number of seconds contained in the current day + * @method int secondsInDecade() Return the number of seconds contained in the current decade + * @method int secondsInHour() Return the number of seconds contained in the current hour + * @method int secondsInMillennium() Return the number of seconds contained in the current millennium + * @method int secondsInMinute() Return the number of seconds contained in the current minute + * @method int secondsInMonth() Return the number of seconds contained in the current month + * @method int secondsInQuarter() Return the number of seconds contained in the current quarter + * @method int secondsInWeek() Return the number of seconds contained in the current week + * @method int secondsInYear() Return the number of seconds contained in the current year + * @method int|static weekOfCentury(?int $week = null) Return the value of the week starting from the beginning of the current century when called with no parameters, change the current week when called with an integer value + * @method int|static weekOfDecade(?int $week = null) Return the value of the week starting from the beginning of the current decade when called with no parameters, change the current week when called with an integer value + * @method int|static weekOfMillennium(?int $week = null) Return the value of the week starting from the beginning of the current millennium when called with no parameters, change the current week when called with an integer value + * @method int|static weekOfMonth(?int $week = null) Return the value of the week starting from the beginning of the current month when called with no parameters, change the current week when called with an integer value + * @method int|static weekOfQuarter(?int $week = null) Return the value of the week starting from the beginning of the current quarter when called with no parameters, change the current week when called with an integer value + * @method int|static weekOfYear(?int $week = null) Return the value of the week starting from the beginning of the current year when called with no parameters, change the current week when called with an integer value + * @method int weeksInCentury() Return the number of weeks contained in the current century + * @method int weeksInDecade() Return the number of weeks contained in the current decade + * @method int weeksInMillennium() Return the number of weeks contained in the current millennium + * @method int weeksInMonth() Return the number of weeks contained in the current month + * @method int weeksInQuarter() Return the number of weeks contained in the current quarter + * @method int|static yearOfCentury(?int $year = null) Return the value of the year starting from the beginning of the current century when called with no parameters, change the current year when called with an integer value + * @method int|static yearOfDecade(?int $year = null) Return the value of the year starting from the beginning of the current decade when called with no parameters, change the current year when called with an integer value + * @method int|static yearOfMillennium(?int $year = null) Return the value of the year starting from the beginning of the current millennium when called with no parameters, change the current year when called with an integer value + * @method int yearsInCentury() Return the number of years contained in the current century + * @method int yearsInDecade() Return the number of years contained in the current decade + * @method int yearsInMillennium() Return the number of years contained in the current millennium * * + * + * @codeCoverageIgnore */ interface CarbonInterface extends DateTimeInterface, JsonSerializable { @@ -581,7 +915,7 @@ interface CarbonInterface extends DateTimeInterface, JsonSerializable /** * Number of X in Y. */ - public const YEARS_PER_MILLENNIUM = 1000; + public const YEARS_PER_MILLENNIUM = 1_000; public const YEARS_PER_CENTURY = 100; public const YEARS_PER_DECADE = 10; public const MONTHS_PER_YEAR = 12; @@ -594,9 +928,9 @@ interface CarbonInterface extends DateTimeInterface, JsonSerializable public const HOURS_PER_DAY = 24; public const MINUTES_PER_HOUR = 60; public const SECONDS_PER_MINUTE = 60; - public const MILLISECONDS_PER_SECOND = 1000; - public const MICROSECONDS_PER_MILLISECOND = 1000; - public const MICROSECONDS_PER_SECOND = 1000000; + public const MILLISECONDS_PER_SECOND = 1_000; + public const MICROSECONDS_PER_MILLISECOND = 1_000; + public const MICROSECONDS_PER_SECOND = 1_000_000; /** * Special settings to get the start of week from current locale culture. @@ -631,6 +965,13 @@ interface CarbonInterface extends DateTimeInterface, JsonSerializable */ public const ISO_FORMAT_REGEXP = '(O[YMDHhms]|[Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY?|g{1,5}|G{1,5}|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?)'; + /** + * Default locale (language and region). + * + * @var string + */ + public const DEFAULT_LOCALE = 'en'; + // /** @@ -640,10 +981,8 @@ interface CarbonInterface extends DateTimeInterface, JsonSerializable * @param array $parameters parameters list * * @throws UnknownMethodException|BadMethodCallException|ReflectionException|Throwable - * - * @return mixed */ - public function __call($method, $parameters); + public function __call(string $method, array $parameters): mixed; /** * Dynamically handle calls to the class. @@ -652,15 +991,13 @@ public function __call($method, $parameters); * @param array $parameters parameters list * * @throws BadMethodCallException - * - * @return mixed */ - public static function __callStatic($method, $parameters); + public static function __callStatic(string $method, array $parameters): mixed; /** * Update constructedObjectId on cloned. */ - public function __clone(); + public function __clone(): void; /** * Create a new Carbon instance. @@ -668,30 +1005,23 @@ public function __clone(); * Please see the testing aids section (specifically static::setTestNow()) * for more on the possibility of this constructor returning a test instance. * - * @param DateTimeInterface|string|null $time - * @param DateTimeZone|string|null $tz - * * @throws InvalidFormatException */ - public function __construct($time = null, $tz = null); + public function __construct(DateTimeInterface|WeekDay|Month|string|int|float|null $time = null, DateTimeZone|string|int|null $timezone = null); /** * Show truthy properties on var_dump(). - * - * @return array */ - public function __debugInfo(); + public function __debugInfo(): array; /** - * Get a part of the Carbon object - * - * @param string $name + * Get a part of the Carbon object. * * @throws UnknownGetterException * * @return string|int|bool|DateTimeZone|null */ - public function __get($name); + public function __get(string $name): mixed; /** * Check if an attribute exists on the object @@ -722,7 +1052,7 @@ public function __set($name, $value); * @return static */ #[ReturnTypeWillChange] - public static function __set_state($dump); + public static function __set_state($dump): static; /** * Returns the list of properties to dump on serialize() called on. @@ -740,8 +1070,6 @@ public function __sleep(); * ``` * echo Carbon::now(); // Carbon instances can be cast to string * ``` - * - * @return string */ public function __toString(); @@ -752,36 +1080,43 @@ public function __toString(); * @example $date->add(15, 'days') * @example $date->add(CarbonInterval::days(4)) * - * @param string|DateInterval|Closure|CarbonConverterInterface $unit - * @param int $value - * @param bool|null $overflow + * @param Unit|string|DateInterval|Closure|CarbonConverterInterface $unit + * @param int|float $value + * @param bool|null $overflow * * @return static */ #[ReturnTypeWillChange] - public function add($unit, $value = 1, $overflow = null); + public function add($unit, $value = 1, ?bool $overflow = null): static; /** + * @deprecated Prefer to use add addUTCUnit() which more accurately defines what it's doing. + * * Add seconds to the instance using timestamp. Positive $value travels * forward while negative $value travels into the past. * - * @param string $unit - * @param int $value + * @param string $unit + * @param int|float|null $value * * @return static */ - public function addRealUnit($unit, $value = 1); + public function addRealUnit(string $unit, $value = 1): static; /** - * Add given units to the current instance. + * Add seconds to the instance using timestamp. Positive $value travels + * forward while negative $value travels into the past. * - * @param string $unit - * @param int $value - * @param bool|null $overflow + * @param string $unit + * @param int|float|null $value * * @return static */ - public function addUnit($unit, $value = 1, $overflow = null); + public function addUTCUnit(string $unit, $value = 1): static; + + /** + * Add given units to the current instance. + */ + public function addUnit(Unit|string $unit, $value = 1, ?bool $overflow = null): static; /** * Add any unit to a new value without overflowing current other unit given. @@ -789,10 +1124,8 @@ public function addUnit($unit, $value = 1, $overflow = null); * @param string $valueUnit unit name to modify * @param int $value amount to add to the input unit * @param string $overflowUnit unit name to not overflow - * - * @return static */ - public function addUnitNoOverflow($valueUnit, $value, $overflowUnit); + public function addUnitNoOverflow(string $valueUnit, int $value, string $overflowUnit): static; /** * Get the difference in a human readable format in the current locale from an other @@ -842,7 +1175,7 @@ public function average($date = null); * * @return static */ - public function avoidMutation(); + public function avoidMutation(): static; /** * Determines if the instance is between two others. @@ -859,13 +1192,9 @@ public function avoidMutation(); * Carbon::parse('2018-07-25')->between('2018-07-25', '2018-08-01', false); // false * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1 - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2 - * @param bool $equal Indicates if an equal to comparison should be done - * - * @return bool + * @param bool $equal Indicates if an equal to comparison should be done */ - public function between($date1, $date2, $equal = true): bool; + public function between(DateTimeInterface|string $date1, DateTimeInterface|string $date2, bool $equal = true): bool; /** * Determines if the instance is between two others, bounds excluded. @@ -876,13 +1205,8 @@ public function between($date1, $date2, $equal = true): bool; * Carbon::parse('2018-07-25')->betweenExcluded('2018-08-01', '2018-08-20'); // false * Carbon::parse('2018-07-25')->betweenExcluded('2018-07-25', '2018-08-01'); // false * ``` - * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1 - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2 - * - * @return bool */ - public function betweenExcluded($date1, $date2): bool; + public function betweenExcluded(DateTimeInterface|string $date1, DateTimeInterface|string $date2): bool; /** * Determines if the instance is between two others, bounds included. @@ -893,13 +1217,8 @@ public function betweenExcluded($date1, $date2): bool; * Carbon::parse('2018-07-25')->betweenIncluded('2018-08-01', '2018-08-20'); // false * Carbon::parse('2018-07-25')->betweenIncluded('2018-07-25', '2018-08-01'); // true * ``` - * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1 - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2 - * - * @return bool */ - public function betweenIncluded($date1, $date2): bool; + public function betweenIncluded(DateTimeInterface|string $date1, DateTimeInterface|string $date2): bool; /** * Returns either day of week + time (e.g. "Last Friday at 3:30 PM") if reference time is within 7 days, @@ -923,13 +1242,8 @@ public function calendar($referenceTime = null, array $formats = []); * Carbon::canBeCreatedFromFormat('11:12:45', 'h:i:s'); // true * Carbon::canBeCreatedFromFormat('13:12:45', 'h:i:s'); // false * ``` - * - * @param string $date - * @param string $format - * - * @return bool */ - public static function canBeCreatedFromFormat($date, $format); + public static function canBeCreatedFromFormat(?string $date, string $format): bool; /** * Return the Carbon instance passed through, a now instance in the same timezone @@ -944,39 +1258,30 @@ public function carbonize($date = null); /** * Cast the current instance into the given class. * - * @param string $className The $className::instance() method will be called to cast the current object. + * @template T + * + * @param class-string $className The $className::instance() method will be called to cast the current object. * - * @return DateTimeInterface + * @return T */ - public function cast(string $className); + public function cast(string $className): mixed; /** * Ceil the current instance second with given precision if specified. - * - * @param float|int|string|\DateInterval|null $precision - * - * @return CarbonInterface */ - public function ceil($precision = 1); + public function ceil(DateInterval|string|int|float $precision = 1): static; /** * Ceil the current instance at the given unit with given precision if specified. - * - * @param string $unit - * @param float|int $precision - * - * @return CarbonInterface */ - public function ceilUnit($unit, $precision = 1); + public function ceilUnit(string $unit, DateInterval|string|int|float $precision = 1): static; /** * Ceil the current instance week. * - * @param int $weekStartsAt optional start allow you to specify the day of week to use to start the week - * - * @return CarbonInterface + * @param WeekDay|int|null $weekStartsAt optional start allow you to specify the day of week to use to start the week */ - public function ceilWeek($weekStartsAt = null); + public function ceilWeek(WeekDay|int|null $weekStartsAt = null): static; /** * Similar to native modify() method of DateTime but can handle more grammars. @@ -990,7 +1295,7 @@ public function ceilWeek($weekStartsAt = null); * * @param string $modifier * - * @return static|false + * @return static */ public function change($modifier); @@ -1041,140 +1346,125 @@ public function copy(); * If $hour is not null then the default values for $minute and $second * will be 0. * - * @param DateTimeInterface|int|null $year - * @param int|null $month - * @param int|null $day - * @param int|null $hour - * @param int|null $minute - * @param int|null $second - * @param DateTimeZone|string|null $tz + * @param DateTimeInterface|string|int|null $year + * @param int|null $month + * @param int|null $day + * @param int|null $hour + * @param int|null $minute + * @param int|null $second + * @param DateTimeZone|string|int|null $timezone * * @throws InvalidFormatException * - * @return static|false + * @return static|null */ - public static function create($year = 0, $month = 1, $day = 1, $hour = 0, $minute = 0, $second = 0, $tz = null); + public static function create($year = 0, $month = 1, $day = 1, $hour = 0, $minute = 0, $second = 0, $timezone = null); /** * Create a Carbon instance from just a date. The time portion is set to now. * - * @param int|null $year - * @param int|null $month - * @param int|null $day - * @param DateTimeZone|string|null $tz + * @param int|null $year + * @param int|null $month + * @param int|null $day + * @param DateTimeZone|string|int|null $timezone * * @throws InvalidFormatException * * @return static */ - public static function createFromDate($year = null, $month = null, $day = null, $tz = null); + public static function createFromDate($year = null, $month = null, $day = null, $timezone = null); /** * Create a Carbon instance from a specific format. * - * @param string $format Datetime format - * @param string $time - * @param DateTimeZone|string|false|null $tz + * @param string $format Datetime format + * @param string $time + * @param DateTimeZone|string|int|null $timezone * * @throws InvalidFormatException * - * @return static|false + * @return static|null */ #[ReturnTypeWillChange] - public static function createFromFormat($format, $time, $tz = null); + public static function createFromFormat($format, $time, $timezone = null); /** * Create a Carbon instance from a specific ISO format (same replacements as ->isoFormat()). * - * @param string $format Datetime format - * @param string $time - * @param DateTimeZone|string|false|null $tz optional timezone - * @param string|null $locale locale to be used for LTS, LT, LL, LLL, etc. macro-formats (en by fault, unneeded if no such macro-format in use) - * @param \Symfony\Component\Translation\TranslatorInterface $translator optional custom translator to use for macro-formats + * @param string $format Datetime format + * @param string $time + * @param DateTimeZone|string|int|null $timezone optional timezone + * @param string|null $locale locale to be used for LTS, LT, LL, LLL, etc. macro-formats (en by fault, unneeded if no such macro-format in use) + * @param TranslatorInterface|null $translator optional custom translator to use for macro-formats * * @throws InvalidFormatException * - * @return static|false + * @return static|null */ - public static function createFromIsoFormat($format, $time, $tz = null, $locale = 'en', $translator = null); + public static function createFromIsoFormat(string $format, string $time, $timezone = null, ?string $locale = 'en', ?TranslatorInterface $translator = null); /** * Create a Carbon instance from a specific format and a string in a given language. * - * @param string $format Datetime format - * @param string $locale - * @param string $time - * @param DateTimeZone|string|false|null $tz + * @param string $format Datetime format + * @param string $locale + * @param string $time + * @param DateTimeZone|string|int|null $timezone * * @throws InvalidFormatException * - * @return static|false + * @return static|null */ - public static function createFromLocaleFormat($format, $locale, $time, $tz = null); + public static function createFromLocaleFormat(string $format, string $locale, string $time, $timezone = null); /** * Create a Carbon instance from a specific ISO format and a string in a given language. * - * @param string $format Datetime ISO format - * @param string $locale - * @param string $time - * @param DateTimeZone|string|false|null $tz + * @param string $format Datetime ISO format + * @param string $locale + * @param string $time + * @param DateTimeZone|string|int|null $timezone * * @throws InvalidFormatException * - * @return static|false + * @return static|null */ - public static function createFromLocaleIsoFormat($format, $locale, $time, $tz = null); + public static function createFromLocaleIsoFormat(string $format, string $locale, string $time, $timezone = null); /** * Create a Carbon instance from just a time. The date portion is set to today. * - * @param int|null $hour - * @param int|null $minute - * @param int|null $second - * @param DateTimeZone|string|null $tz + * @param int|null $hour + * @param int|null $minute + * @param int|null $second + * @param DateTimeZone|string|int|null $timezone * * @throws InvalidFormatException * * @return static */ - public static function createFromTime($hour = 0, $minute = 0, $second = 0, $tz = null); + public static function createFromTime($hour = 0, $minute = 0, $second = 0, $timezone = null): static; /** * Create a Carbon instance from a time string. The date portion is set to today. * - * @param string $time - * @param DateTimeZone|string|null $tz - * * @throws InvalidFormatException - * - * @return static */ - public static function createFromTimeString($time, $tz = null); + public static function createFromTimeString(string $time, DateTimeZone|string|int|null $timezone = null): static; /** - * Create a Carbon instance from a timestamp and set the timezone (use default one if not specified). + * Create a Carbon instance from a timestamp and set the timezone (UTC by default). * * Timestamp input can be given as int, float or a string containing one or more numbers. - * - * @param float|int|string $timestamp - * @param \DateTimeZone|string|null $tz - * - * @return static */ - public static function createFromTimestamp($timestamp, $tz = null); + public static function createFromTimestamp(string|int|float $timestamp, DateTimeZone|string|int|null $timezone = null): static; /** * Create a Carbon instance from a timestamp in milliseconds. * * Timestamp input can be given as int, float or a string containing one or more numbers. - * - * @param float|int|string $timestamp - * @param \DateTimeZone|string|null $tz - * - * @return static */ - public static function createFromTimestampMs($timestamp, $tz = null); + public static function createFromTimestampMs(string|int|float $timestamp, DateTimeZone|string|int|null $timezone = null): static; /** * Create a Carbon instance from a timestamp in milliseconds. @@ -1185,32 +1475,28 @@ public static function createFromTimestampMs($timestamp, $tz = null); * * @return static */ - public static function createFromTimestampMsUTC($timestamp); + public static function createFromTimestampMsUTC($timestamp): static; /** - * Create a Carbon instance from an timestamp keeping the timezone to UTC. + * Create a Carbon instance from a timestamp keeping the timezone to UTC. * * Timestamp input can be given as int, float or a string containing one or more numbers. - * - * @param float|int|string $timestamp - * - * @return static */ - public static function createFromTimestampUTC($timestamp); + public static function createFromTimestampUTC(string|int|float $timestamp): static; /** * Create a Carbon instance from just a date. The time portion is set to midnight. * - * @param int|null $year - * @param int|null $month - * @param int|null $day - * @param DateTimeZone|string|null $tz + * @param int|null $year + * @param int|null $month + * @param int|null $day + * @param DateTimeZone|string|int|null $timezone * * @throws InvalidFormatException * * @return static */ - public static function createMidnightDate($year = null, $month = null, $day = null, $tz = null); + public static function createMidnightDate($year = null, $month = null, $day = null, $timezone = null); /** * Create a new safe Carbon instance from a specific date and time. @@ -1227,47 +1513,53 @@ public static function createMidnightDate($year = null, $month = null, $day = nu * If one of the set values is not valid, an InvalidDateException * will be thrown. * - * @param int|null $year - * @param int|null $month - * @param int|null $day - * @param int|null $hour - * @param int|null $minute - * @param int|null $second - * @param DateTimeZone|string|null $tz + * @param int|null $year + * @param int|null $month + * @param int|null $day + * @param int|null $hour + * @param int|null $minute + * @param int|null $second + * @param DateTimeZone|string|int|null $timezone * * @throws InvalidDateException * - * @return static|false + * @return static|null */ - public static function createSafe($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $tz = null); + public static function createSafe($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $timezone = null); /** * Create a new Carbon instance from a specific date and time using strict validation. * * @see create() * - * @param int|null $year - * @param int|null $month - * @param int|null $day - * @param int|null $hour - * @param int|null $minute - * @param int|null $second - * @param DateTimeZone|string|null $tz + * @param int|null $year + * @param int|null $month + * @param int|null $day + * @param int|null $hour + * @param int|null $minute + * @param int|null $second + * @param DateTimeZone|string|int|null $timezone * * @throws InvalidFormatException * * @return static */ - public static function createStrict(?int $year = 0, ?int $month = 1, ?int $day = 1, ?int $hour = 0, ?int $minute = 0, ?int $second = 0, $tz = null); + public static function createStrict(?int $year = 0, ?int $month = 1, ?int $day = 1, ?int $hour = 0, ?int $minute = 0, ?int $second = 0, $timezone = null): static; /** * Get/set the day of year. * + * @template T of int|null + * * @param int|null $value new value for day of year if using as setter. * + * @psalm-param T $value + * * @return static|int + * + * @psalm-return (T is int ? static : int) */ - public function dayOfYear($value = null); + public function dayOfYear(?int $value = null): static|int; /** * Get the difference as a CarbonInterval instance. @@ -1279,7 +1571,19 @@ public function dayOfYear($value = null); * * @return CarbonInterval */ - public function diffAsCarbonInterval($date = null, $absolute = true, array $skip = []); + public function diffAsCarbonInterval($date = null, bool $absolute = false, array $skip = []): CarbonInterval; + + /** + * Get the difference as a DateInterval instance. + * Return relative interval (negative if $absolute flag is not set to true and the given date is before + * current one). + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return DateInterval + */ + public function diffAsDateInterval($date = null, bool $absolute = false): DateInterval; /** * Get the difference by the given interval using a filter closure. @@ -1291,7 +1595,7 @@ public function diffAsCarbonInterval($date = null, $absolute = true, array $skip * * @return int */ - public function diffFiltered(CarbonInterval $ci, Closure $callback, $date = null, $absolute = true); + public function diffFiltered(CarbonInterval $ci, Closure $callback, $date = null, bool $absolute = false): int; /** * Get the difference in a human readable format in the current locale from current instance to an other @@ -1306,54 +1610,58 @@ public function diffFiltered(CarbonInterval $ci, Closure $callback, $date = null * echo Carbon::tomorrow()->diffForHumans(Carbon::yesterday(), ['short' => true]) . "\n"; * ``` * - * @param Carbon|\DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below; - * if null passed, now will be used as comparison reference; - * if any other type, it will be converted to date and used as reference. - * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains: - * - 'syntax' entry (see below) - * - 'short' entry (see below) - * - 'parts' entry (see below) - * - 'options' entry (see below) - * - 'skip' entry, list of units to skip (array of strings or a single string, - * ` it can be the unit name (singular or plural) or its shortcut - * ` (y, m, w, d, h, min, s, ms, µs). - * - 'aUnit' entry, prefer "an hour" over "1 hour" if true - * - 'join' entry determines how to join multiple parts of the string - * ` - if $join is a string, it's used as a joiner glue - * ` - if $join is a callable/closure, it get the list of string and should return a string - * ` - if $join is an array, the first item will be the default glue, and the second item - * ` will be used instead of the glue for the last item - * ` - if $join is true, it will be guessed from the locale ('list' translation file entry) - * ` - if $join is missing, a space will be used as glue - * - 'other' entry (see above) - * - 'minimumUnit' entry determines the smallest unit of time to display can be long or - * ` short form of the units, e.g. 'hour' or 'h' (default value: s) - * if int passed, it add modifiers: - * Possible values: - * - CarbonInterface::DIFF_ABSOLUTE no modifiers - * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier - * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier - * Default value: CarbonInterface::DIFF_ABSOLUTE - * @param bool $short displays short format of time units - * @param int $parts maximum number of parts to display (default value: 1: single unit) - * @param int $options human diff options - * - * @return string - */ - public function diffForHumans($other = null, $syntax = null, $short = false, $parts = 1, $options = null); - - /** - * Get the difference in days rounded down. + * @param Carbon|DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below; + * if null passed, now will be used as comparison reference; + * if any other type, it will be converted to date and used as reference. + * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains: + * ⦿ 'syntax' entry (see below) + * ⦿ 'short' entry (see below) + * ⦿ 'parts' entry (see below) + * ⦿ 'options' entry (see below) + * ⦿ 'skip' entry, list of units to skip (array of strings or a single string, + * ` it can be the unit name (singular or plural) or its shortcut + * ` (y, m, w, d, h, min, s, ms, µs). + * ⦿ 'aUnit' entry, prefer "an hour" over "1 hour" if true + * ⦿ 'altNumbers' entry, use alternative numbers if available + * ` (from the current language if true is passed, from the given language(s) + * ` if array or string is passed) + * ⦿ 'join' entry determines how to join multiple parts of the string + * ` - if $join is a string, it's used as a joiner glue + * ` - if $join is a callable/closure, it get the list of string and should return a string + * ` - if $join is an array, the first item will be the default glue, and the second item + * ` will be used instead of the glue for the last item + * ` - if $join is true, it will be guessed from the locale ('list' translation file entry) + * ` - if $join is missing, a space will be used as glue + * ⦿ 'other' entry (see above) + * ⦿ 'minimumUnit' entry determines the smallest unit of time to display can be long or + * ` short form of the units, e.g. 'hour' or 'h' (default value: s) + * ⦿ 'locale' language in which the diff should be output (has no effect if 'translator' key is set) + * ⦿ 'translator' a custom translator to use to translator the output. + * if int passed, it adds modifiers: + * Possible values: + * - CarbonInterface::DIFF_ABSOLUTE no modifiers + * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier + * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier + * Default value: CarbonInterface::DIFF_ABSOLUTE + * @param bool $short displays short format of time units + * @param int $parts maximum number of parts to display (default value: 1: single unit) + * @param int $options human diff options + */ + public function diffForHumans($other = null, $syntax = null, $short = false, $parts = 1, $options = null): string; + + /** + * Get the difference in days. * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference + * @param bool $utc Always convert dates to UTC before comparing (if not set, it will do it only if timezones are different) * - * @return int + * @return float */ - public function diffInDays($date = null, $absolute = true); + public function diffInDays($date = null, bool $absolute = false, bool $utc = false): float; /** - * Get the difference in days using a filter closure rounded down. + * Get the difference in days using a filter closure. * * @param Closure $callback * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date @@ -1361,20 +1669,20 @@ public function diffInDays($date = null, $absolute = true); * * @return int */ - public function diffInDaysFiltered(Closure $callback, $date = null, $absolute = true); + public function diffInDaysFiltered(Closure $callback, $date = null, bool $absolute = false): int; /** - * Get the difference in hours rounded down. + * Get the difference in hours. * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference * - * @return int + * @return float */ - public function diffInHours($date = null, $absolute = true); + public function diffInHours($date = null, bool $absolute = false): float; /** - * Get the difference in hours using a filter closure rounded down. + * Get the difference in hours using a filter closure. * * @param Closure $callback * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date @@ -1382,7 +1690,7 @@ public function diffInHours($date = null, $absolute = true); * * @return int */ - public function diffInHoursFiltered(Closure $callback, $date = null, $absolute = true); + public function diffInHoursFiltered(Closure $callback, $date = null, bool $absolute = false): int; /** * Get the difference in microseconds. @@ -1390,167 +1698,129 @@ public function diffInHoursFiltered(Closure $callback, $date = null, $absolute = * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference * - * @return int - */ - public function diffInMicroseconds($date = null, $absolute = true); - - /** - * Get the difference in milliseconds rounded down. - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return int - */ - public function diffInMilliseconds($date = null, $absolute = true); - - /** - * Get the difference in minutes rounded down. - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return int - */ - public function diffInMinutes($date = null, $absolute = true); - - /** - * Get the difference in months rounded down. - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return int - */ - public function diffInMonths($date = null, $absolute = true); - - /** - * Get the difference in quarters rounded down. - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return int + * @return float */ - public function diffInQuarters($date = null, $absolute = true); + public function diffInMicroseconds($date = null, bool $absolute = false): float; /** - * Get the difference in hours rounded down using timestamps. + * Get the difference in milliseconds. * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference * - * @return int + * @return float */ - public function diffInRealHours($date = null, $absolute = true); + public function diffInMilliseconds($date = null, bool $absolute = false): float; /** - * Get the difference in microseconds using timestamps. + * Get the difference in minutes. * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference * - * @return int + * @return float */ - public function diffInRealMicroseconds($date = null, $absolute = true); + public function diffInMinutes($date = null, bool $absolute = false): float; /** - * Get the difference in milliseconds rounded down using timestamps. + * Get the difference in months. * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference + * @param bool $utc Always convert dates to UTC before comparing (if not set, it will do it only if timezones are different) * - * @return int + * @return float */ - public function diffInRealMilliseconds($date = null, $absolute = true); + public function diffInMonths($date = null, bool $absolute = false, bool $utc = false): float; /** - * Get the difference in minutes rounded down using timestamps. + * Get the difference in quarters. * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference + * @param bool $utc Always convert dates to UTC before comparing (if not set, it will do it only if timezones are different) * - * @return int + * @return float */ - public function diffInRealMinutes($date = null, $absolute = true); + public function diffInQuarters($date = null, bool $absolute = false, bool $utc = false): float; /** - * Get the difference in seconds using timestamps. + * Get the difference in seconds. * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference * - * @return int + * @return float */ - public function diffInRealSeconds($date = null, $absolute = true); + public function diffInSeconds($date = null, bool $absolute = false): float; /** - * Get the difference in seconds rounded down. - * + * @param Unit|string $unit microsecond, millisecond, second, minute, + * hour, day, week, month, quarter, year, + * century, millennium * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference + * @param bool $utc Always convert dates to UTC before comparing (if not set, it will do it only if timezones are different) * - * @return int + * @return float */ - public function diffInSeconds($date = null, $absolute = true); + public function diffInUnit(Unit|string $unit, $date = null, bool $absolute = false, bool $utc = false): float; /** - * Get the difference in weekdays rounded down. + * Get the difference in weekdays. * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference * * @return int */ - public function diffInWeekdays($date = null, $absolute = true); + public function diffInWeekdays($date = null, bool $absolute = false): int; /** - * Get the difference in weekend days using a filter rounded down. + * Get the difference in weekend days using a filter. * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference * * @return int */ - public function diffInWeekendDays($date = null, $absolute = true); + public function diffInWeekendDays($date = null, bool $absolute = false): int; /** - * Get the difference in weeks rounded down. + * Get the difference in weeks. * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference + * @param bool $utc Always convert dates to UTC before comparing (if not set, it will do it only if timezones are different) * - * @return int + * @return float */ - public function diffInWeeks($date = null, $absolute = true); + public function diffInWeeks($date = null, bool $absolute = false, bool $utc = false): float; /** * Get the difference in years * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference + * @param bool $utc Always convert dates to UTC before comparing (if not set, it will do it only if timezones are different) * - * @return int + * @return float */ - public function diffInYears($date = null, $absolute = true); + public function diffInYears($date = null, bool $absolute = false, bool $utc = false): float; /** * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. * You should rather use the ->settings() method. * @see settings - * - * @param int $humanDiffOption */ - public static function disableHumanDiffOption($humanDiffOption); + public static function disableHumanDiffOption(int $humanDiffOption): void; /** * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. * You should rather use the ->settings() method. * @see settings - * - * @param int $humanDiffOption */ - public static function enableHumanDiffOption($humanDiffOption); + public static function enableHumanDiffOption(int $humanDiffOption): void; /** * Modify to end of current given unit. @@ -1558,16 +1828,11 @@ public static function enableHumanDiffOption($humanDiffOption); * @example * ``` * echo Carbon::parse('2018-07-25 12:45:16.334455') - * ->startOf('month') - * ->endOf('week', Carbon::FRIDAY); + * ->startOf(Unit::Month) + * ->endOf(Unit::Week, Carbon::FRIDAY); * ``` - * - * @param string $unit - * @param array $params - * - * @return static */ - public function endOf($unit, ...$params); + public function endOf(Unit|string $unit, mixed ...$params): static; /** * Resets the date to end of the century and time to 23:59:59.999999 @@ -1612,10 +1877,8 @@ public function endOfDecade(); * ``` * echo Carbon::parse('2018-07-25 12:45:16')->endOfHour(); * ``` - * - * @return static */ - public function endOfHour(); + public function endOfHour(): static; /** * Resets the date to end of the millennium and time to 23:59:59.999999 @@ -1629,6 +1892,18 @@ public function endOfHour(); */ public function endOfMillennium(); + /** + * Modify to end of current millisecond, microseconds such as 12345 become 123999 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16.334455') + * ->endOfSecond() + * ->format('H:i:s.u'); + * ``` + */ + public function endOfMillisecond(): static; + /** * Modify to end of current minute, seconds become 59 * @@ -1636,10 +1911,8 @@ public function endOfMillennium(); * ``` * echo Carbon::parse('2018-07-25 12:45:16')->endOfMinute(); * ``` - * - * @return static */ - public function endOfMinute(); + public function endOfMinute(): static; /** * Resets the date to end of the month and time to 23:59:59.999999 @@ -1674,10 +1947,8 @@ public function endOfQuarter(); * ->endOfSecond() * ->format('H:i:s.u'); * ``` - * - * @return static */ - public function endOfSecond(); + public function endOfSecond(): static; /** * Resets the date to end of week (defined in $weekEndsAt) and time to 23:59:59.999999 @@ -1689,11 +1960,11 @@ public function endOfSecond(); * echo Carbon::parse('2018-07-25 12:45:16')->endOfWeek(Carbon::SATURDAY) . "\n"; * ``` * - * @param int $weekEndsAt optional start allow you to specify the day of week to use to end the week + * @param WeekDay|int|null $weekEndsAt optional end allow you to specify the day of week to use to end the week * * @return static */ - public function endOfWeek($weekEndsAt = null); + public function endOfWeek(WeekDay|int|null $weekEndsAt = null): static; /** * Resets the date to end of the year and time to 23:59:59.999999 @@ -1717,13 +1988,9 @@ public function endOfYear(); * Carbon::parse('2018-07-25 12:45:16')->eq('2018-07-25 12:45:17'); // false * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * * @see equalTo() - * - * @return bool */ - public function eq($date): bool; + public function eq(DateTimeInterface|string $date): bool; /** * Determines if the instance is equal to another @@ -1734,12 +2001,8 @@ public function eq($date): bool; * Carbon::parse('2018-07-25 12:45:16')->equalTo(Carbon::parse('2018-07-25 12:45:16')); // true * Carbon::parse('2018-07-25 12:45:16')->equalTo('2018-07-25 12:45:17'); // false * ``` - * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * - * @return bool */ - public function equalTo($date): bool; + public function equalTo(DateTimeInterface|string $date): bool; /** * Set the current locale to the given, execute the passed function, reset the locale to previous one, @@ -1750,7 +2013,7 @@ public function equalTo($date): bool; * * @return mixed */ - public static function executeWithLocale($locale, $func); + public static function executeWithLocale(string $locale, callable $func): mixed; /** * Get the farthest date from the instance (second-precision). @@ -1799,192 +2062,27 @@ public function firstOfQuarter($dayOfWeek = null); public function firstOfYear($dayOfWeek = null); /** - * Get the difference in days as float (microsecond-precision). - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return float - */ - public function floatDiffInDays($date = null, $absolute = true); - - /** - * Get the difference in hours as float (microsecond-precision). - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return float + * Round the current instance second with given precision if specified. */ - public function floatDiffInHours($date = null, $absolute = true); + public function floor(DateInterval|string|int|float $precision = 1): static; /** - * Get the difference in minutes as float (microsecond-precision). - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return float + * Truncate the current instance at the given unit with given precision if specified. */ - public function floatDiffInMinutes($date = null, $absolute = true); + public function floorUnit(string $unit, DateInterval|string|int|float $precision = 1): static; /** - * Get the difference in months as float (microsecond-precision). - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference + * Truncate the current instance week. * - * @return float + * @param WeekDay|int|null $weekStartsAt optional start allow you to specify the day of week to use to start the week */ - public function floatDiffInMonths($date = null, $absolute = true); + public function floorWeek(WeekDay|int|null $weekStartsAt = null): static; /** - * Get the difference in days as float (microsecond-precision). + * @alias diffForHumans * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return float - */ - public function floatDiffInRealDays($date = null, $absolute = true); - - /** - * Get the difference in hours as float (microsecond-precision) using timestamps. - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return float - */ - public function floatDiffInRealHours($date = null, $absolute = true); - - /** - * Get the difference in minutes as float (microsecond-precision) using timestamps. - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return float - */ - public function floatDiffInRealMinutes($date = null, $absolute = true); - - /** - * Get the difference in months as float (microsecond-precision) using timestamps. - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return float - */ - public function floatDiffInRealMonths($date = null, $absolute = true); - - /** - * Get the difference in seconds as float (microsecond-precision) using timestamps. - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return float - */ - public function floatDiffInRealSeconds($date = null, $absolute = true); - - /** - * Get the difference in weeks as float (microsecond-precision). - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return float - */ - public function floatDiffInRealWeeks($date = null, $absolute = true); - - /** - * Get the difference in year as float (microsecond-precision) using timestamps. - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return float - */ - public function floatDiffInRealYears($date = null, $absolute = true); - - /** - * Get the difference in seconds as float (microsecond-precision). - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return float - */ - public function floatDiffInSeconds($date = null, $absolute = true); - - /** - * Get the difference in weeks as float (microsecond-precision). - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return float - */ - public function floatDiffInWeeks($date = null, $absolute = true); - - /** - * Get the difference in year as float (microsecond-precision). - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return float - */ - public function floatDiffInYears($date = null, $absolute = true); - - /** - * Round the current instance second with given precision if specified. - * - * @param float|int|string|\DateInterval|null $precision - * - * @return CarbonInterface - */ - public function floor($precision = 1); - - /** - * Truncate the current instance at the given unit with given precision if specified. - * - * @param string $unit - * @param float|int $precision - * - * @return CarbonInterface - */ - public function floorUnit($unit, $precision = 1); - - /** - * Truncate the current instance week. - * - * @param int $weekStartsAt optional start allow you to specify the day of week to use to start the week - * - * @return CarbonInterface - */ - public function floorWeek($weekStartsAt = null); - - /** - * Format the instance with the current locale. You can set the current - * locale using setlocale() https://php.net/setlocale. - * - * @deprecated It uses OS language package and strftime() which is deprecated since PHP 8.1. - * Use ->isoFormat() instead. - * Deprecated since 2.55.0 - * - * @param string $format - * - * @return string - */ - public function formatLocalized($format); - - /** - * @alias diffForHumans - * - * Get the difference in a human readable format in the current locale from current instance to an other - * instance given (or now if null given). + * Get the difference in a human readable format in the current locale from current instance to an other + * instance given (or now if null given). * * @param Carbon|\DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below; * if null passed, now will be used as comparison reference; @@ -2055,35 +2153,31 @@ public function fromNow($syntax = null, $short = false, $parts = 1, $options = n * * @return static */ - public static function fromSerialized($value); + public static function fromSerialized($value): static; /** * Register a custom macro. * - * @param object|callable $macro - * @param int $priority marco with higher priority is tried first + * @param callable $macro + * @param int $priority marco with higher priority is tried first * * @return void */ - public static function genericMacro($macro, $priority = 0); + public static function genericMacro(callable $macro, int $priority = 0): void; /** - * Get a part of the Carbon object - * - * @param string $name + * Get a part of the Carbon object. * * @throws UnknownGetterException * - * @return string|int|bool|DateTimeZone|null + * @return string|int|bool|DateTimeZone */ - public function get($name); + public function get(Unit|string $name): mixed; /** * Returns the alternative number for a given date property if available in the current locale. * * @param string $key date property - * - * @return string */ public function getAltNumber(string $key): string; @@ -2107,108 +2201,81 @@ public static function getAvailableLocalesInfo(); * Returns list of calendar formats for ISO formatting. * * @param string|null $locale current locale used if null - * - * @return array */ - public function getCalendarFormats($locale = null); + public function getCalendarFormats(?string $locale = null): array; + + public function getClock(): ?WrapperClock; /** - * Get the days of the week - * - * @return array + * Get the days of the week. */ - public static function getDays(); + public static function getDays(): array; /** * Return the number of days since the start of the week (using the current locale or the first parameter * if explicitly given). * - * @param int|null $weekStartsAt optional start allow you to specify the day of week to use to start the week, - * if not provided, start of week is inferred from the locale - * (Sunday for en_US, Monday for de_DE, etc.) - * - * @return int + * @param WeekDay|int|null $weekStartsAt optional start allow you to specify the day of week to use to start the week, + * if not provided, start of week is inferred from the locale + * (Sunday for en_US, Monday for de_DE, etc.) */ - public function getDaysFromStartOfWeek(?int $weekStartsAt = null): int; + public function getDaysFromStartOfWeek(WeekDay|int|null $weekStartsAt = null): int; /** * Get the fallback locale. * * @see https://symfony.com/doc/current/components/translation.html#fallback-locales - * - * @return string|null */ - public static function getFallbackLocale(); + public static function getFallbackLocale(): ?string; /** * List of replacements from date() format to isoFormat(). - * - * @return array */ - public static function getFormatsToIsoReplacements(); + public static function getFormatsToIsoReplacements(): array; /** * Return default humanDiff() options (merged flags as integer). - * - * @return int */ - public static function getHumanDiffOptions(); + public static function getHumanDiffOptions(): int; /** * Returns list of locale formats for ISO formatting. * * @param string|null $locale current locale used if null - * - * @return array */ - public function getIsoFormats($locale = null); + public function getIsoFormats(?string $locale = null): array; /** * Returns list of locale units for ISO formatting. - * - * @return array */ - public static function getIsoUnits(); + public static function getIsoUnits(): array; /** * {@inheritdoc} - * - * @return array */ - #[ReturnTypeWillChange] - public static function getLastErrors(); + public static function getLastErrors(): array|false; /** * Get the raw callable macro registered globally or locally for a given name. - * - * @param string $name - * - * @return callable|null */ - public function getLocalMacro($name); + public function getLocalMacro(string $name): ?callable; /** * Get the translator of the current instance or the default if none set. - * - * @return \Symfony\Component\Translation\TranslatorInterface */ - public function getLocalTranslator(); + public function getLocalTranslator(): TranslatorInterface; /** * Get the current translator locale. * * @return string */ - public static function getLocale(); + public static function getLocale(): string; /** * Get the raw callable macro registered globally for a given name. - * - * @param string $name - * - * @return callable|null */ - public static function getMacro($name); + public static function getMacro(string $name): ?callable; /** * get midday/noon hour @@ -2224,10 +2291,8 @@ public static function getMidDayAt(); * like "-12:00". * * @param string $separator string to place between hours and minutes (":" by default) - * - * @return string */ - public function getOffsetString($separator = ':'); + public function getOffsetString(string $separator = ':'): string; /** * Returns a unit of the instance padded with 0 by default or any other string if specified. @@ -2236,10 +2301,8 @@ public function getOffsetString($separator = ':'); * @param int $length Length of the output (2 by default) * @param string $padString String to use for padding ("0" by default) * @param int $padType Side(s) to pad (STR_PAD_LEFT by default) - * - * @return string */ - public function getPaddedUnit($unit, $length = 2, $padString = '0', $padType = 0); + public function getPaddedUnit($unit, $length = 2, $padString = '0', $padType = 0): string; /** * Returns a timestamp rounded with the given precision (6 by default). @@ -2259,38 +2322,34 @@ public function getPaddedUnit($unit, $length = 2, $padString = '0', $padType = 0 * * @return float */ - public function getPreciseTimestamp($precision = 6); + public function getPreciseTimestamp($precision = 6): float; /** * Returns current local settings. - * - * @return array */ - public function getSettings(); + public function getSettings(): array; /** * Get the Carbon instance (real or mock) to be returned when a "now" * instance is created. * - * @return Closure|static the current instance used for testing + * @return Closure|self|null the current instance used for testing */ - public static function getTestNow(); + public static function getTestNow(): Closure|self|null; /** * Return a format from H:i to H:i:s.u according to given unit precision. * * @param string $unitPrecision "minute", "second", "millisecond" or "microsecond" - * - * @return string */ - public static function getTimeFormatByPrecision($unitPrecision); + public static function getTimeFormatByPrecision(string $unitPrecision): string; /** * Returns the timestamp with millisecond precision. * * @return int */ - public function getTimestampMs(); + public function getTimestampMs(): int; /** * Get the translation of the current week day name (with context for languages with multiple forms). @@ -2298,19 +2357,15 @@ public function getTimestampMs(); * @param string|null $context whole format string * @param string $keySuffix "", "_short" or "_min" * @param string|null $defaultValue default value if translation missing - * - * @return string */ - public function getTranslatedDayName($context = null, $keySuffix = '', $defaultValue = null); + public function getTranslatedDayName(?string $context = null, string $keySuffix = '', ?string $defaultValue = null): string; /** * Get the translation of the current abbreviated week day name (with context for languages with multiple forms). * * @param string|null $context whole format string - * - * @return string */ - public function getTranslatedMinDayName($context = null); + public function getTranslatedMinDayName(?string $context = null): string; /** * Get the translation of the current month day name (with context for languages with multiple forms). @@ -2318,36 +2373,30 @@ public function getTranslatedMinDayName($context = null); * @param string|null $context whole format string * @param string $keySuffix "" or "_short" * @param string|null $defaultValue default value if translation missing - * - * @return string */ - public function getTranslatedMonthName($context = null, $keySuffix = '', $defaultValue = null); + public function getTranslatedMonthName(?string $context = null, string $keySuffix = '', ?string $defaultValue = null): string; /** * Get the translation of the current short week day name (with context for languages with multiple forms). * * @param string|null $context whole format string - * - * @return string */ - public function getTranslatedShortDayName($context = null); + public function getTranslatedShortDayName(?string $context = null): string; /** * Get the translation of the current short month day name (with context for languages with multiple forms). * * @param string|null $context whole format string - * - * @return string */ - public function getTranslatedShortMonthName($context = null); + public function getTranslatedShortMonthName(?string $context = null): string; /** * Returns raw translation message for a given key. * - * @param string $key key to find - * @param string|null $locale current locale used if null - * @param string|null $default default value if translation returns the key - * @param \Symfony\Component\Translation\TranslatorInterface $translator an optional translator to use + * @param string $key key to find + * @param string|null $locale current locale used if null + * @param string|null $default default value if translation returns the key + * @param TranslatorInterface $translator an optional translator to use * * @return string */ @@ -2356,42 +2405,40 @@ public function getTranslationMessage(string $key, ?string $locale = null, ?stri /** * Returns raw translation message for a given key. * - * @param \Symfony\Component\Translation\TranslatorInterface $translator the translator to use - * @param string $key key to find - * @param string|null $locale current locale used if null - * @param string|null $default default value if translation returns the key + * @param TranslatorInterface|null $translator the translator to use + * @param string $key key to find + * @param string|null $locale current locale used if null + * @param string|null $default default value if translation returns the key * - * @return string + * @return string|Closure|null */ public static function getTranslationMessageWith($translator, string $key, ?string $locale = null, ?string $default = null); /** - * Get the default translator instance in use. - * - * @return \Symfony\Component\Translation\TranslatorInterface + * Initialize the default translator instance if necessary. */ - public static function getTranslator(); + public static function getTranslator(): TranslatorInterface; /** - * Get the last day of week + * Get the last day of week. + * + * @param string $locale local to consider the last day of week. * * @return int */ - public static function getWeekEndsAt(); + public static function getWeekEndsAt(?string $locale = null): int; /** - * Get the first day of week + * Get the first day of week. * * @return int */ - public static function getWeekStartsAt(); + public static function getWeekStartsAt(?string $locale = null): int; /** * Get weekend days - * - * @return array */ - public static function getWeekendDays(); + public static function getWeekendDays(): array; /** * Determines if the instance is greater (after) than another @@ -2402,12 +2449,8 @@ public static function getWeekendDays(); * Carbon::parse('2018-07-25 12:45:16')->greaterThan('2018-07-25 12:45:16'); // false * Carbon::parse('2018-07-25 12:45:16')->greaterThan('2018-07-25 12:45:17'); // false * ``` - * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * - * @return bool */ - public function greaterThan($date): bool; + public function greaterThan(DateTimeInterface|string $date): bool; /** * Determines if the instance is greater (after) than or equal to another @@ -2418,12 +2461,8 @@ public function greaterThan($date): bool; * Carbon::parse('2018-07-25 12:45:16')->greaterThanOrEqualTo('2018-07-25 12:45:16'); // true * Carbon::parse('2018-07-25 12:45:16')->greaterThanOrEqualTo('2018-07-25 12:45:17'); // false * ``` - * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * - * @return bool */ - public function greaterThanOrEqualTo($date): bool; + public function greaterThanOrEqualTo(DateTimeInterface|string $date): bool; /** * Determines if the instance is greater (after) than another @@ -2435,13 +2474,9 @@ public function greaterThanOrEqualTo($date): bool; * Carbon::parse('2018-07-25 12:45:16')->gt('2018-07-25 12:45:17'); // false * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * * @see greaterThan() - * - * @return bool */ - public function gt($date): bool; + public function gt(DateTimeInterface|string $date): bool; /** * Determines if the instance is greater (after) than or equal to another @@ -2453,13 +2488,9 @@ public function gt($date): bool; * Carbon::parse('2018-07-25 12:45:16')->gte('2018-07-25 12:45:17'); // false * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * * @see greaterThanOrEqualTo() - * - * @return bool */ - public function gte($date): bool; + public function gte(DateTimeInterface|string $date): bool; /** * Checks if the (date)time string is in a given format. @@ -2469,13 +2500,8 @@ public function gte($date): bool; * Carbon::hasFormat('11:12:45', 'h:i:s'); // true * Carbon::hasFormat('13:12:45', 'h:i:s'); // false * ``` - * - * @param string $date - * @param string $format - * - * @return bool */ - public static function hasFormat($date, $format); + public static function hasFormat(string $date, string $format): bool; /** * Checks if the (date)time string is in a given format. @@ -2491,23 +2517,17 @@ public static function hasFormat($date, $format); * * @return bool */ - public static function hasFormatWithModifiers($date, $format): bool; + public static function hasFormatWithModifiers(?string $date, string $format): bool; /** * Checks if macro is registered globally or locally. - * - * @param string $name - * - * @return bool */ - public function hasLocalMacro($name); + public function hasLocalMacro(string $name): bool; /** * Return true if the current instance has its own translator. - * - * @return bool */ - public function hasLocalTranslator(); + public function hasLocalTranslator(): bool; /** * Checks if macro is registered globally. @@ -2516,16 +2536,14 @@ public function hasLocalTranslator(); * * @return bool */ - public static function hasMacro($name); + public static function hasMacro(string $name): bool; /** * Determine if a time string will produce a relative date. * - * @param string $time - * * @return bool true if time match a relative date, false if absolute or invalid time string */ - public static function hasRelativeKeywords($time); + public static function hasRelativeKeywords(?string $time): bool; /** * Determine if there is a valid test instance set. A valid test instance @@ -2533,16 +2551,12 @@ public static function hasRelativeKeywords($time); * * @return bool true if there is a test instance, otherwise false */ - public static function hasTestNow(); + public static function hasTestNow(): bool; /** * Create a Carbon instance from a DateTime one. - * - * @param DateTimeInterface $date - * - * @return static */ - public static function instance($date); + public static function instance(DateTimeInterface $date): static; /** * Returns true if the current date matches the given string. @@ -2565,10 +2579,8 @@ public static function instance($date); * ``` * * @param string $tester day name, month name, hour, date, etc. as string - * - * @return bool */ - public function is(string $tester); + public function is(WeekDay|Month|string $tester): bool; /** * Determines if the instance is greater (after) than another @@ -2580,13 +2592,9 @@ public function is(string $tester); * Carbon::parse('2018-07-25 12:45:16')->isAfter('2018-07-25 12:45:17'); // false * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * * @see greaterThan() - * - * @return bool */ - public function isAfter($date): bool; + public function isAfter(DateTimeInterface|string $date): bool; /** * Determines if the instance is less (before) than another @@ -2598,13 +2606,9 @@ public function isAfter($date): bool; * Carbon::parse('2018-07-25 12:45:16')->isBefore('2018-07-25 12:45:17'); // true * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * * @see lessThan() - * - * @return bool */ - public function isBefore($date): bool; + public function isBefore(DateTimeInterface|string $date): bool; /** * Determines if the instance is between two others @@ -2617,13 +2621,9 @@ public function isBefore($date): bool; * Carbon::parse('2018-07-25')->isBetween('2018-07-25', '2018-08-01', false); // false * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1 - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2 - * @param bool $equal Indicates if an equal to comparison should be done - * - * @return bool + * @param bool $equal Indicates if an equal to comparison should be done */ - public function isBetween($date1, $date2, $equal = true): bool; + public function isBetween(DateTimeInterface|string $date1, DateTimeInterface|string $date2, bool $equal = true): bool; /** * Check if its the birthday. Compares the date/month values of the two dates. @@ -2636,11 +2636,11 @@ public function isBetween($date1, $date2, $equal = true): bool; * Carbon::parse('2019-06-05')->isBirthday(Carbon::parse('2001-06-06')); // false * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use current day. + * @param DateTimeInterface|string|null $date The instance to compare with or null to use current day. * * @return bool */ - public function isBirthday($date = null); + public function isBirthday(DateTimeInterface|string|null $date = null): bool; /** * Determines if the instance is in the current unit given. @@ -2654,10 +2654,8 @@ public function isBirthday($date = null); * @param string $unit The unit to test. * * @throws BadMethodCallException - * - * @return bool */ - public function isCurrentUnit($unit); + public function isCurrentUnit(string $unit): bool; /** * Checks if this day is a specific day of the week. @@ -2670,11 +2668,16 @@ public function isCurrentUnit($unit); * Carbon::parse('2019-07-17')->isDayOfWeek('Friday'); // false * ``` * - * @param int $dayOfWeek + * @param int|string $dayOfWeek * * @return bool */ - public function isDayOfWeek($dayOfWeek); + public function isDayOfWeek($dayOfWeek): bool; + + /** + * Determines if the instance is end of century (last day by default but interval can be customized). + */ + public function isEndOfCentury(Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null): bool; /** * Check if the instance is end of day. @@ -2690,11 +2693,53 @@ public function isDayOfWeek($dayOfWeek); * Carbon::parse('2019-02-28 23:59:59')->isEndOfDay(true); // false * ``` * - * @param bool $checkMicroseconds check time at microseconds precision - * - * @return bool + * @param bool $checkMicroseconds check time at microseconds precision + * @param Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval if an interval is specified it will be used as precision + * for instance with "15 minutes", it checks if current date-time + * is in the last 15 minutes of the day, with Unit::Hour, it + * checks if it's in the last hour of the day. + */ + public function isEndOfDay(Unit|DateInterval|Closure|CarbonConverterInterface|string|bool $checkMicroseconds = false, Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null): bool; + + /** + * Determines if the instance is end of decade (last day by default but interval can be customized). + */ + public function isEndOfDecade(Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null): bool; + + /** + * Determines if the instance is end of hour (last microsecond by default but interval can be customized). + */ + public function isEndOfHour(Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null): bool; + + /** + * Determines if the instance is end of millennium (last day by default but interval can be customized). + */ + public function isEndOfMillennium(Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null): bool; + + /** + * Determines if the instance is end of millisecond (last microsecond by default but interval can be customized). + */ + public function isEndOfMillisecond(Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null): bool; + + /** + * Determines if the instance is end of minute (last microsecond by default but interval can be customized). + */ + public function isEndOfMinute(Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null): bool; + + /** + * Determines if the instance is end of month (last day by default but interval can be customized). + */ + public function isEndOfMonth(Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null): bool; + + /** + * Determines if the instance is end of quarter (last day by default but interval can be customized). + */ + public function isEndOfQuarter(Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null): bool; + + /** + * Determines if the instance is end of second (last microsecond by default but interval can be customized). */ - public function isEndOfDay($checkMicroseconds = false); + public function isEndOfSecond(Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null): bool; /** * Returns true if the date was created using CarbonImmutable::endOfTime() @@ -2703,6 +2748,33 @@ public function isEndOfDay($checkMicroseconds = false); */ public function isEndOfTime(): bool; + /** + * Check if the instance is end of a given unit (tolerating a given interval). + * + * @example + * ``` + * // Check if a date-time is the last 15 minutes of the hour it's in + * Carbon::parse('2019-02-28 20:13:00')->isEndOfUnit(Unit::Hour, '15 minutes'); // false + * ``` + */ + public function isEndOfUnit(Unit $unit, Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, mixed ...$params): bool; + + /** + * Determines if the instance is end of week (last day by default but interval can be customized). + * + * @example + * ``` + * Carbon::parse('2024-08-31')->endOfWeek()->isEndOfWeek(); // true + * Carbon::parse('2024-08-31')->isEndOfWeek(); // false + * ``` + */ + public function isEndOfWeek(Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, WeekDay|int|null $weekEndsAt = null): bool; + + /** + * Determines if the instance is end of year (last day by default but interval can be customized). + */ + public function isEndOfYear(Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null): bool; + /** * Determines if the instance is in the future, ie. greater (after) than now. * @@ -2711,17 +2783,13 @@ public function isEndOfTime(): bool; * Carbon::now()->addHours(5)->isFuture(); // true * Carbon::now()->subHours(5)->isFuture(); // false * ``` - * - * @return bool */ - public function isFuture(); + public function isFuture(): bool; /** * Returns true if the current class/instance is immutable. - * - * @return bool */ - public static function isImmutable(); + public static function isImmutable(): bool; /** * Check if today is the last day of the Month @@ -2734,10 +2802,8 @@ public static function isImmutable(); * Carbon::parse('2019-03-31')->isLastOfMonth(); // true * Carbon::parse('2019-04-30')->isLastOfMonth(); // true * ``` - * - * @return bool */ - public function isLastOfMonth(); + public function isLastOfMonth(): bool; /** * Determines if the instance is a leap year. @@ -2747,10 +2813,8 @@ public function isLastOfMonth(); * Carbon::parse('2020-01-01')->isLeapYear(); // true * Carbon::parse('2019-01-01')->isLeapYear(); // false * ``` - * - * @return bool */ - public function isLeapYear(); + public function isLeapYear(): bool; /** * Determines if the instance is a long year (using ISO 8601 year). @@ -2765,10 +2829,8 @@ public function isLeapYear(); * ``` * * @see https://en.wikipedia.org/wiki/ISO_8601#Week_dates - * - * @return bool */ - public function isLongIsoYear(); + public function isLongIsoYear(): bool; /** * Determines if the instance is a long year (using calendar year). @@ -2785,10 +2847,8 @@ public function isLongIsoYear(); * ``` * * @see https://en.wikipedia.org/wiki/ISO_8601#Week_dates - * - * @return bool */ - public function isLongYear(); + public function isLongYear(): bool; /** * Check if the instance is midday. @@ -2800,10 +2860,8 @@ public function isLongYear(); * Carbon::parse('2019-02-28 12:00:00.999999')->isMidday(); // true * Carbon::parse('2019-02-28 12:00:01')->isMidday(); // false * ``` - * - * @return bool */ - public function isMidday(); + public function isMidday(): bool; /** * Check if the instance is start of day / midnight. @@ -2814,10 +2872,8 @@ public function isMidday(); * Carbon::parse('2019-02-28 00:00:00.999999')->isMidnight(); // true * Carbon::parse('2019-02-28 00:00:01')->isMidnight(); // false * ``` - * - * @return bool */ - public function isMidnight(); + public function isMidnight(): bool; /** * Returns true if a property can be changed via setter. @@ -2826,14 +2882,36 @@ public function isMidnight(); * * @return bool */ - public static function isModifiableUnit($unit); + public static function isModifiableUnit($unit): bool; /** * Returns true if the current class/instance is mutable. + */ + public static function isMutable(): bool; + + /** + * Determines if the instance is now or in the future, ie. greater (after) than or equal to now. * - * @return bool + * @example + * ``` + * Carbon::now()->isNowOrFuture(); // true + * Carbon::now()->addHours(5)->isNowOrFuture(); // true + * Carbon::now()->subHours(5)->isNowOrFuture(); // false + * ``` + */ + public function isNowOrFuture(): bool; + + /** + * Determines if the instance is now or in the past, ie. less (before) than or equal to now. + * + * @example + * ``` + * Carbon::now()->isNowOrPast(); // true + * Carbon::now()->subHours(5)->isNowOrPast(); // true + * Carbon::now()->addHours(5)->isNowOrPast(); // false + * ``` */ - public static function isMutable(); + public function isNowOrPast(): bool; /** * Determines if the instance is in the past, ie. less (before) than now. @@ -2843,10 +2921,8 @@ public static function isMutable(); * Carbon::now()->subHours(5)->isPast(); // true * Carbon::now()->addHours(5)->isPast(); // false * ``` - * - * @return bool */ - public function isPast(); + public function isPast(): bool; /** * Compares the formatted values of the two dates. @@ -2857,12 +2933,10 @@ public function isPast(); * Carbon::parse('2019-06-13')->isSameAs('Y-d', Carbon::parse('2019-06-14')); // false * ``` * - * @param string $format date formats to compare. - * @param \Carbon\Carbon|\DateTimeInterface|string|null $date instance to compare with or null to use current day. - * - * @return bool + * @param string $format date formats to compare. + * @param DateTimeInterface|string $date instance to compare with or null to use current day. */ - public function isSameAs($format, $date = null); + public function isSameAs(string $format, DateTimeInterface|string $date): bool; /** * Checks if the passed in date is in the same month as the instance´s month. @@ -2875,12 +2949,12 @@ public function isSameAs($format, $date = null); * Carbon::parse('2019-01-12')->isSameMonth(Carbon::parse('2018-01-01'), false); // true * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use the current date. - * @param bool $ofSameYear Check if it is the same month in the same year. + * @param DateTimeInterface|string $date The instance to compare with or null to use the current date. + * @param bool $ofSameYear Check if it is the same month in the same year. * * @return bool */ - public function isSameMonth($date = null, $ofSameYear = true); + public function isSameMonth(DateTimeInterface|string $date, bool $ofSameYear = true): bool; /** * Checks if the passed in date is in the same quarter as the instance quarter (and year if needed). @@ -2893,12 +2967,12 @@ public function isSameMonth($date = null, $ofSameYear = true); * Carbon::parse('2019-01-12')->isSameQuarter(Carbon::parse('2018-03-01'), false); // true * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|string|null $date The instance to compare with or null to use current day. - * @param bool $ofSameYear Check if it is the same month in the same year. + * @param DateTimeInterface|string $date The instance to compare with or null to use current day. + * @param bool $ofSameYear Check if it is the same month in the same year. * * @return bool */ - public function isSameQuarter($date = null, $ofSameYear = true); + public function isSameQuarter(DateTimeInterface|string $date, bool $ofSameYear = true): bool; /** * Determines if the instance is in the current unit given. @@ -2909,14 +2983,19 @@ public function isSameQuarter($date = null, $ofSameYear = true); * Carbon::parse('2018-12-13')->isSameUnit('year', Carbon::parse('2019-12-25')); // false * ``` * - * @param string $unit singular unit string - * @param \Carbon\Carbon|\DateTimeInterface|null $date instance to compare with or null to use current day. + * @param string $unit singular unit string + * @param DateTimeInterface|string $date instance to compare with or null to use current day. * * @throws BadComparisonUnitException * * @return bool */ - public function isSameUnit($unit, $date = null); + public function isSameUnit(string $unit, DateTimeInterface|string $date): bool; + + /** + * Determines if the instance is start of century (first day by default but interval can be customized). + */ + public function isStartOfCentury(Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null): bool; /** * Check if the instance is start of day / midnight. @@ -2930,11 +3009,53 @@ public function isSameUnit($unit, $date = null); * Carbon::parse('2019-02-28 00:00:00.000012')->isStartOfDay(true); // false * ``` * - * @param bool $checkMicroseconds check time at microseconds precision - * - * @return bool + * @param bool $checkMicroseconds check time at microseconds precision + * @param Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval if an interval is specified it will be used as precision + * for instance with "15 minutes", it checks if current date-time + * is in the last 15 minutes of the day, with Unit::Hour, it + * checks if it's in the last hour of the day. + */ + public function isStartOfDay(Unit|DateInterval|Closure|CarbonConverterInterface|string|bool $checkMicroseconds = false, Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null): bool; + + /** + * Determines if the instance is start of decade (first day by default but interval can be customized). + */ + public function isStartOfDecade(Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null): bool; + + /** + * Determines if the instance is start of hour (first microsecond by default but interval can be customized). + */ + public function isStartOfHour(Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null): bool; + + /** + * Determines if the instance is start of millennium (first day by default but interval can be customized). + */ + public function isStartOfMillennium(Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null): bool; + + /** + * Determines if the instance is start of millisecond (first microsecond by default but interval can be customized). + */ + public function isStartOfMillisecond(Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null): bool; + + /** + * Determines if the instance is start of minute (first microsecond by default but interval can be customized). + */ + public function isStartOfMinute(Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null): bool; + + /** + * Determines if the instance is start of month (first day by default but interval can be customized). + */ + public function isStartOfMonth(Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null): bool; + + /** + * Determines if the instance is start of quarter (first day by default but interval can be customized). */ - public function isStartOfDay($checkMicroseconds = false); + public function isStartOfQuarter(Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null): bool; + + /** + * Determines if the instance is start of second (first microsecond by default but interval can be customized). + */ + public function isStartOfSecond(Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null): bool; /** * Returns true if the date was created using CarbonImmutable::startOfTime() @@ -2943,13 +3064,40 @@ public function isStartOfDay($checkMicroseconds = false); */ public function isStartOfTime(): bool; + /** + * Check if the instance is start of a given unit (tolerating a given interval). + * + * @example + * ``` + * // Check if a date-time is the first 15 minutes of the hour it's in + * Carbon::parse('2019-02-28 20:13:00')->isStartOfUnit(Unit::Hour, '15 minutes'); // true + * ``` + */ + public function isStartOfUnit(Unit $unit, Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, mixed ...$params): bool; + + /** + * Determines if the instance is start of week (first day by default but interval can be customized). + * + * @example + * ``` + * Carbon::parse('2024-08-31')->startOfWeek()->isStartOfWeek(); // true + * Carbon::parse('2024-08-31')->isStartOfWeek(); // false + * ``` + */ + public function isStartOfWeek(Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, WeekDay|int|null $weekStartsAt = null): bool; + + /** + * Determines if the instance is start of year (first day by default but interval can be customized). + */ + public function isStartOfYear(Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null): bool; + /** * Returns true if the strict mode is globally in use, false else. * (It can be overridden in specific instances.) * * @return bool */ - public static function isStrictModeEnabled(); + public static function isStrictModeEnabled(): bool; /** * Determines if the instance is today. @@ -2959,10 +3107,8 @@ public static function isStrictModeEnabled(); * Carbon::today()->isToday(); // true * Carbon::tomorrow()->isToday(); // false * ``` - * - * @return bool */ - public function isToday(); + public function isToday(): bool; /** * Determines if the instance is tomorrow. @@ -2972,10 +3118,8 @@ public function isToday(); * Carbon::tomorrow()->isTomorrow(); // true * Carbon::yesterday()->isTomorrow(); // false * ``` - * - * @return bool */ - public function isTomorrow(); + public function isTomorrow(): bool; /** * Determines if the instance is a weekday. @@ -2985,10 +3129,8 @@ public function isTomorrow(); * Carbon::parse('2019-07-14')->isWeekday(); // false * Carbon::parse('2019-07-15')->isWeekday(); // true * ``` - * - * @return bool */ - public function isWeekday(); + public function isWeekday(): bool; /** * Determines if the instance is a weekend day. @@ -2998,10 +3140,8 @@ public function isWeekday(); * Carbon::parse('2019-07-14')->isWeekend(); // true * Carbon::parse('2019-07-15')->isWeekend(); // false * ``` - * - * @return bool */ - public function isWeekend(); + public function isWeekend(): bool; /** * Determines if the instance is yesterday. @@ -3011,18 +3151,13 @@ public function isWeekend(); * Carbon::yesterday()->isYesterday(); // true * Carbon::tomorrow()->isYesterday(); // false * ``` - * - * @return bool */ - public function isYesterday(); + public function isYesterday(): bool; /** * Format in the current language using ISO replacement patterns. * - * @param string $format * @param string|null $originalFormat provide context if a chunk has been passed alone - * - * @return string */ public function isoFormat(string $format, ?string $originalFormat = null): string; @@ -3055,11 +3190,9 @@ public function isoWeekYear($year = null, $dayOfWeek = null, $dayOfYear = null); /** * Get/set the ISO weekday from 1 (Monday) to 7 (Sunday). * - * @param int|null $value new value for weekday if using as setter. - * - * @return static|int + * @param WeekDay|int|null $value new value for weekday if using as setter. */ - public function isoWeekday($value = null); + public function isoWeekday(WeekDay|int|null $value = null): static|int; /** * Get the number of weeks of the current week-year using given first day of week and first @@ -3075,11 +3208,8 @@ public function isoWeeksInYear($dayOfWeek = null, $dayOfYear = null); /** * Prepare the object for JSON serialization. - * - * @return array|string */ - #[ReturnTypeWillChange] - public function jsonSerialize(); + public function jsonSerialize(): mixed; /** * Modify to the last occurrence of a given day of the week @@ -3126,12 +3256,8 @@ public function lastOfYear($dayOfWeek = null); * Carbon::parse('2018-07-25 12:45:16')->lessThan('2018-07-25 12:45:16'); // false * Carbon::parse('2018-07-25 12:45:16')->lessThan('2018-07-25 12:45:17'); // true * ``` - * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * - * @return bool */ - public function lessThan($date): bool; + public function lessThan(DateTimeInterface|string $date): bool; /** * Determines if the instance is less (before) or equal to another @@ -3142,12 +3268,8 @@ public function lessThan($date): bool; * Carbon::parse('2018-07-25 12:45:16')->lessThanOrEqualTo('2018-07-25 12:45:16'); // true * Carbon::parse('2018-07-25 12:45:16')->lessThanOrEqualTo('2018-07-25 12:45:17'); // true * ``` - * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * - * @return bool */ - public function lessThanOrEqualTo($date): bool; + public function lessThanOrEqualTo(DateTimeInterface|string $date): bool; /** * Get/set the locale for the current instance. @@ -3157,7 +3279,7 @@ public function lessThanOrEqualTo($date): bool; * * @return $this|string */ - public function locale(?string $locale = null, ...$fallbackLocales); + public function locale(?string $locale = null, string ...$fallbackLocales): static|string; /** * Returns true if the given locale is internally supported and has words for 1-day diff (just now, yesterday, tomorrow). @@ -3167,7 +3289,7 @@ public function locale(?string $locale = null, ...$fallbackLocales); * * @return bool */ - public static function localeHasDiffOneDayWords($locale); + public static function localeHasDiffOneDayWords(string $locale): bool; /** * Returns true if the given locale is internally supported and has diff syntax support (ago, from now, before, after). @@ -3177,7 +3299,7 @@ public static function localeHasDiffOneDayWords($locale); * * @return bool */ - public static function localeHasDiffSyntax($locale); + public static function localeHasDiffSyntax(string $locale): bool; /** * Returns true if the given locale is internally supported and has words for 2-days diff (before yesterday, after tomorrow). @@ -3187,7 +3309,7 @@ public static function localeHasDiffSyntax($locale); * * @return bool */ - public static function localeHasDiffTwoDayWords($locale); + public static function localeHasDiffTwoDayWords(string $locale): bool; /** * Returns true if the given locale is internally supported and has period syntax support (X times, every X, from X, to X). @@ -3207,7 +3329,7 @@ public static function localeHasPeriodSyntax($locale); * * @return bool */ - public static function localeHasShortUnits($locale); + public static function localeHasShortUnits(string $locale): bool; /** * Determines if the instance is less (before) than another @@ -3219,13 +3341,9 @@ public static function localeHasShortUnits($locale); * Carbon::parse('2018-07-25 12:45:16')->lt('2018-07-25 12:45:17'); // true * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * * @see lessThan() - * - * @return bool */ - public function lt($date): bool; + public function lt(DateTimeInterface|string $date): bool; /** * Determines if the instance is less (before) or equal to another @@ -3237,17 +3355,15 @@ public function lt($date): bool; * Carbon::parse('2018-07-25 12:45:16')->lte('2018-07-25 12:45:17'); // true * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * * @see lessThanOrEqualTo() - * - * @return bool */ - public function lte($date): bool; + public function lte(DateTimeInterface|string $date): bool; /** * Register a custom macro. * + * Pass null macro to remove it. + * * @example * ``` * $userSettings = [ @@ -3260,12 +3376,9 @@ public function lte($date): bool; * echo Carbon::yesterday()->hours(11)->userFormat(); * ``` * - * @param string $name - * @param object|callable $macro - * - * @return void + * @param-closure-this static $macro */ - public static function macro($name, $macro); + public static function macro(string $name, ?callable $macro): void; /** * Make a Carbon instance from given variable if possible. @@ -3279,7 +3392,7 @@ public static function macro($name, $macro); * * @return static|null */ - public static function make($var); + public static function make($var, DateTimeZone|string|null $timezone = null); /** * Get the maximum instance between a given instance (default now) and the current instance. @@ -3290,13 +3403,6 @@ public static function make($var); */ public function max($date = null); - /** - * Create a Carbon instance for the greatest supported date. - * - * @return static - */ - public static function maxValue(); - /** * Get the maximum instance between a given instance (default now) and the current instance. * @@ -3312,8 +3418,6 @@ public function maximum($date = null); * Return the meridiem of the current time in the current locale. * * @param bool $isLower if true, returns lowercase variant if available in the current locale. - * - * @return string */ public function meridiem(bool $isLower = false): string; @@ -3333,13 +3437,6 @@ public function midDay(); */ public function min($date = null); - /** - * Create a Carbon instance for the lowest supported date. - * - * @return static - */ - public static function minValue(); - /** * Get the minimum instance between a given instance (default now) and the current instance. * @@ -3376,20 +3473,16 @@ public function minimum($date = null); * echo "$previousBlackMoon\n"; * ``` * - * @param object|string $mixin - * * @throws ReflectionException - * - * @return void */ - public static function mixin($mixin); + public static function mixin(object|string $mixin): void; /** * Calls \DateTime::modify if mutable or \DateTimeImmutable::modify else. * * @see https://php.net/manual/en/datetime.modify.php * - * @return static|false + * @return static */ #[ReturnTypeWillChange] public function modify($modify); @@ -3404,13 +3497,9 @@ public function modify($modify); * Carbon::parse('2018-07-25 12:45:16')->ne('2018-07-25 12:45:17'); // true * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * * @see notEqualTo() - * - * @return bool */ - public function ne($date): bool; + public function ne(DateTimeInterface|string $date): bool; /** * Modify to the next occurrence of a given modifier such as a day of @@ -3420,7 +3509,7 @@ public function ne($date): bool; * * @param string|int|null $modifier * - * @return static|false + * @return static */ public function next($modifier = null); @@ -3447,28 +3536,20 @@ public function nextWeekendDay(); * Carbon::parse('2018-07-25 12:45:16')->notEqualTo(Carbon::parse('2018-07-25 12:45:16')); // false * Carbon::parse('2018-07-25 12:45:16')->notEqualTo('2018-07-25 12:45:17'); // true * ``` - * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * - * @return bool */ - public function notEqualTo($date): bool; + public function notEqualTo(DateTimeInterface|string $date): bool; /** * Get a Carbon instance for the current date and time. - * - * @param DateTimeZone|string|null $tz - * - * @return static */ - public static function now($tz = null); + public static function now(DateTimeZone|string|int|null $timezone = null): static; /** * Returns a present instance in the same timezone. * * @return static */ - public function nowWithSameTz(); + public function nowWithSameTz(): static; /** * Modify to the given occurrence of a given day of the week @@ -3511,11 +3592,6 @@ public function nthOfYear($nth, $dayOfWeek); /** * Return a property with its ordinal. - * - * @param string $key - * @param string|null $period - * - * @return string */ public function ordinal(string $key, ?string $period = null): string; @@ -3526,35 +3602,24 @@ public function ordinal(string $key, ?string $period = null): string; * as it allows you to do Carbon::parse('Monday next week')->fn() rather * than (new Carbon('Monday next week'))->fn(). * - * @param string|DateTimeInterface|null $time - * @param DateTimeZone|string|null $tz - * * @throws InvalidFormatException - * - * @return static */ - public static function parse($time = null, $tz = null); + public static function parse(DateTimeInterface|WeekDay|Month|string|int|float|null $time, DateTimeZone|string|int|null $timezone = null): static; /** * Create a carbon instance from a localized string (in French, Japanese, Arabic, etc.). * - * @param string $time date/time string in the given language (may also contain English). - * @param string|null $locale if locale is null or not specified, current global locale will be - * used instead. - * @param DateTimeZone|string|null $tz optional timezone for the new instance. + * @param string $time date/time string in the given language (may also contain English). + * @param string|null $locale if locale is null or not specified, current global locale will be + * used instead. + * @param DateTimeZone|string|int|null $timezone optional timezone for the new instance. * * @throws InvalidFormatException - * - * @return static */ - public static function parseFromLocale($time, $locale = null, $tz = null); + public static function parseFromLocale(string $time, ?string $locale = null, DateTimeZone|string|int|null $timezone = null): static; /** * Returns standardized plural of a given singular/plural unit name (in English). - * - * @param string $unit - * - * @return string */ public static function pluralUnit(string $unit): string; @@ -3566,7 +3631,7 @@ public static function pluralUnit(string $unit): string; * * @param string|int|null $modifier * - * @return static|false + * @return static */ public function previous($modifier = null); @@ -3590,10 +3655,8 @@ public function previousWeekendDay(); * @param \DateTimeInterface|Carbon|CarbonImmutable|null $end period end date * @param int|\DateInterval|string|null $interval period default interval or number of the given $unit * @param string|null $unit if specified, $interval must be an integer - * - * @return CarbonPeriod */ - public function range($end = null, $interval = null, $unit = null); + public function range($end = null, $interval = null, $unit = null): CarbonPeriod; /** * Call native PHP DateTime/DateTimeImmutable add() method. @@ -3602,29 +3665,25 @@ public function range($end = null, $interval = null, $unit = null); * * @return static */ - public function rawAdd(DateInterval $interval); + public function rawAdd(DateInterval $interval): static; /** * Create a Carbon instance from a specific format. * - * @param string $format Datetime format - * @param string $time - * @param DateTimeZone|string|false|null $tz + * @param string $format Datetime format + * @param string $time + * @param DateTimeZone|string|int|null $timezone * * @throws InvalidFormatException * - * @return static|false + * @return static|null */ - public static function rawCreateFromFormat($format, $time, $tz = null); + public static function rawCreateFromFormat(string $format, string $time, $timezone = null); /** * @see https://php.net/manual/en/datetime.format.php - * - * @param string $format - * - * @return string */ - public function rawFormat($format); + public function rawFormat(string $format): string; /** * Create a carbon instance from a string. @@ -3633,28 +3692,19 @@ public function rawFormat($format); * as it allows you to do Carbon::parse('Monday next week')->fn() rather * than (new Carbon('Monday next week'))->fn(). * - * @param string|DateTimeInterface|null $time - * @param DateTimeZone|string|null $tz - * * @throws InvalidFormatException - * - * @return static */ - public static function rawParse($time = null, $tz = null); + public static function rawParse(DateTimeInterface|WeekDay|Month|string|int|float|null $time, DateTimeZone|string|int|null $timezone = null): static; /** * Call native PHP DateTime/DateTimeImmutable sub() method. - * - * @param DateInterval $interval - * - * @return static */ - public function rawSub(DateInterval $interval); + public function rawSub(DateInterval $interval): static; /** * Remove all macros and generic macros. */ - public static function resetMacros(); + public static function resetMacros(): void; /** * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. @@ -3667,14 +3717,14 @@ public static function resetMacros(); * * @return void */ - public static function resetMonthsOverflow(); + public static function resetMonthsOverflow(): void; /** * Reset the format used to the default when type juggling a Carbon instance to a string * * @return void */ - public static function resetToStringFormat(); + public static function resetToStringFormat(): void; /** * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. @@ -3687,142 +3737,93 @@ public static function resetToStringFormat(); * * @return void */ - public static function resetYearsOverflow(); + public static function resetYearsOverflow(): void; /** * Round the current instance second with given precision if specified. - * - * @param float|int|string|\DateInterval|null $precision - * @param string $function - * - * @return CarbonInterface */ - public function round($precision = 1, $function = 'round'); + public function round(DateInterval|string|int|float $precision = 1, callable|string $function = 'round'): static; /** * Round the current instance at the given unit with given precision if specified and the given function. - * - * @param string $unit - * @param float|int $precision - * @param string $function - * - * @return CarbonInterface */ - public function roundUnit($unit, $precision = 1, $function = 'round'); + public function roundUnit(string $unit, DateInterval|string|int|float $precision = 1, callable|string $function = 'round'): static; /** * Round the current instance week. * - * @param int $weekStartsAt optional start allow you to specify the day of week to use to start the week - * - * @return CarbonInterface + * @param WeekDay|int|null $weekStartsAt optional start allow you to specify the day of week to use to start the week */ - public function roundWeek($weekStartsAt = null); + public function roundWeek(WeekDay|int|null $weekStartsAt = null): static; /** * The number of seconds since midnight. * - * @return int + * @return float */ - public function secondsSinceMidnight(); + public function secondsSinceMidnight(): float; /** * The number of seconds until 23:59:59. * - * @return int + * @return float */ - public function secondsUntilEndOfDay(); + public function secondsUntilEndOfDay(): float; /** * Return a serialized string of the instance. - * - * @return string */ - public function serialize(); + public function serialize(): string; /** * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. * You should rather transform Carbon object before the serialization. * * JSON serialize all Carbon instances using the given callback. - * - * @param callable $callback - * - * @return void */ - public static function serializeUsing($callback); + public static function serializeUsing(callable|string|null $format): void; /** - * Set a part of the Carbon object - * - * @param string|array $name - * @param string|int|DateTimeZone $value + * Set a part of the Carbon object. * * @throws ImmutableException|UnknownSetterException * * @return $this */ - public function set($name, $value = null); + public function set(Unit|array|string $name, DateTimeZone|Month|string|int|float|null $value = null): static; /** * Set the date with gregorian year, month and day numbers. * * @see https://php.net/manual/en/datetime.setdate.php - * - * @param int $year - * @param int $month - * @param int $day - * - * @return static */ - #[ReturnTypeWillChange] - public function setDate($year, $month, $day); + public function setDate(int $year, int $month, int $day): static; /** * Set the year, month, and date for this instance to that of the passed instance. - * - * @param Carbon|DateTimeInterface $date now if null - * - * @return static */ - public function setDateFrom($date = null); + public function setDateFrom(DateTimeInterface|string $date): static; /** * Set the date and time all together. - * - * @param int $year - * @param int $month - * @param int $day - * @param int $hour - * @param int $minute - * @param int $second - * @param int $microseconds - * - * @return static */ - public function setDateTime($year, $month, $day, $hour, $minute, $second = 0, $microseconds = 0); + public function setDateTime(int $year, int $month, int $day, int $hour, int $minute, int $second = 0, int $microseconds = 0): static; /** * Set the date and time for this instance to that of the passed instance. - * - * @param Carbon|DateTimeInterface $date - * - * @return static */ - public function setDateTimeFrom($date = null); + public function setDateTimeFrom(DateTimeInterface|string $date): static; /** * Set the day (keeping the current time) to the start of the week + the number of days passed as the first * parameter. First day of week is driven by the locale unless explicitly set with the second parameter. * - * @param int $numberOfDays number of days to add after the start of the current week - * @param int|null $weekStartsAt optional start allow you to specify the day of week to use to start the week, - * if not provided, start of week is inferred from the locale - * (Sunday for en_US, Monday for de_DE, etc.) - * - * @return static + * @param int $numberOfDays number of days to add after the start of the current week + * @param WeekDay|int|null $weekStartsAt optional start allow you to specify the day of week to use to start the week, + * if not provided, start of week is inferred from the locale + * (Sunday for en_US, Monday for de_DE, etc.) */ - public function setDaysFromStartOfWeek(int $numberOfDays, ?int $weekStartsAt = null); + public function setDaysFromStartOfWeek(int $numberOfDays, WeekDay|int|null $weekStartsAt = null): static; /** * Set the fallback locale. @@ -3831,49 +3832,34 @@ public function setDaysFromStartOfWeek(int $numberOfDays, ?int $weekStartsAt = n * * @param string $locale */ - public static function setFallbackLocale($locale); + public static function setFallbackLocale(string $locale): void; /** * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. * You should rather use the ->settings() method. * @see settings - * - * @param int $humanDiffOptions */ - public static function setHumanDiffOptions($humanDiffOptions); + public static function setHumanDiffOptions(int $humanDiffOptions): void; /** * Set a date according to the ISO 8601 standard - using weeks and day offsets rather than specific dates. * * @see https://php.net/manual/en/datetime.setisodate.php - * - * @param int $year - * @param int $week - * @param int $day - * - * @return static */ - #[ReturnTypeWillChange] - public function setISODate($year, $week, $day = 1); + public function setISODate(int $year, int $week, int $day = 1): static; /** * Set the translator for the current instance. - * - * @param \Symfony\Component\Translation\TranslatorInterface $translator - * - * @return $this */ public function setLocalTranslator(TranslatorInterface $translator); /** * Set the current translator locale and indicate if the source locale file exists. - * Pass 'auto' as locale to use closest language from the current LC_TIME locale. + * Pass 'auto' as locale to use the closest language to the current LC_TIME locale. * * @param string $locale locale ex. en - * - * @return bool */ - public static function setLocale($locale); + public static function setLocale(string $locale): void; /** * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. @@ -3913,7 +3899,7 @@ public static function setMidDayAt($hour); * * @param DateTimeInterface|Closure|static|string|false|null $testNow real or mock Carbon instance */ - public static function setTestNow($testNow = null); + public static function setTestNow(mixed $testNow = null): void; /** * Set a Carbon instance (real or mock) to be returned when a "now" @@ -3934,62 +3920,36 @@ public static function setTestNow($testNow = null); * * @param DateTimeInterface|Closure|static|string|false|null $testNow real or mock Carbon instance */ - public static function setTestNowAndTimezone($testNow = null, $tz = null); + public static function setTestNowAndTimezone($testNow = null, $timezone = null): void; /** * Resets the current time of the DateTime object to a different time. * * @see https://php.net/manual/en/datetime.settime.php - * - * @param int $hour - * @param int $minute - * @param int $second - * @param int $microseconds - * - * @return static */ - #[ReturnTypeWillChange] - public function setTime($hour, $minute, $second = 0, $microseconds = 0); + public function setTime(int $hour, int $minute, int $second = 0, int $microseconds = 0): static; /** * Set the hour, minute, second and microseconds for this instance to that of the passed instance. - * - * @param Carbon|DateTimeInterface $date now if null - * - * @return static */ - public function setTimeFrom($date = null); + public function setTimeFrom(DateTimeInterface|string $date): static; /** * Set the time by time string. - * - * @param string $time - * - * @return static */ - public function setTimeFromTimeString($time); + public function setTimeFromTimeString(string $time): static; /** * Set the instance's timestamp. * * Timestamp input can be given as int, float or a string containing one or more numbers. - * - * @param float|int|string $unixTimestamp - * - * @return static */ - #[ReturnTypeWillChange] - public function setTimestamp($unixTimestamp); + public function setTimestamp(string|int|float $timestamp): static; /** * Set the instance's timezone from a string or object. - * - * @param DateTimeZone|string $value - * - * @return static */ - #[ReturnTypeWillChange] - public function setTimezone($value); + public function setTimezone(DateTimeZone|string|int $timeZone): static; /** * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. @@ -4003,26 +3963,24 @@ public function setTimezone($value); * * @return void */ - public static function setToStringFormat($format); + public static function setToStringFormat(Closure|string|null $format): void; /** * Set the default translator instance to use. * - * @param \Symfony\Component\Translation\TranslatorInterface $translator + * @param TranslatorInterface $translator * * @return void */ - public static function setTranslator(TranslatorInterface $translator); + public static function setTranslator(TranslatorInterface $translator): void; /** * Set specified unit to new given value. * - * @param string $unit year, month, day, hour, minute, second or microsecond - * @param int $value new value for given unit - * - * @return static + * @param string $unit year, month, day, hour, minute, second or microsecond + * @param Month|int $value new value for given unit */ - public function setUnit($unit, $value = null); + public function setUnit(string $unit, Month|int|float|null $value = null): static; /** * Set any unit to a new value without overflowing current other unit given. @@ -4030,49 +3988,8 @@ public function setUnit($unit, $value = null); * @param string $valueUnit unit name to modify * @param int $value new value for the input unit * @param string $overflowUnit unit name to not overflow - * - * @return static - */ - public function setUnitNoOverflow($valueUnit, $value, $overflowUnit); - - /** - * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use UTF-8 language packages on every machine. - * - * Set if UTF8 will be used for localized date/time. - * - * @param bool $utf8 - */ - public static function setUtf8($utf8); - - /** - * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * Use $weekStartsAt optional parameter instead when using startOfWeek, floorWeek, ceilWeek - * or roundWeek method. You can also use the 'first_day_of_week' locale setting to change the - * start of week according to current locale selected and implicitly the end of week. - * - * Set the last day of week - * - * @param int|string $day week end day (or 'auto' to get the day before the first day of week - * from Carbon::getLocale() culture). - * - * @return void - */ - public static function setWeekEndsAt($day); - - /** - * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * Use $weekEndsAt optional parameter instead when using endOfWeek method. You can also use the - * 'first_day_of_week' locale setting to change the start of week according to current locale - * selected and implicitly the end of week. - * - * Set the first day of week - * - * @param int|string $day week start day (or 'auto' to get the first day of week from Carbon::getLocale() culture). - * - * @return void */ - public static function setWeekStartsAt($day); + public function setUnitNoOverflow(string $valueUnit, int $value, string $overflowUnit): static; /** * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. @@ -4096,12 +4013,8 @@ public static function setWeekStartsAt($day); * ``` * * Set weekend days - * - * @param array $days - * - * @return void */ - public static function setWeekendDays($days); + public static function setWeekendDays(array $days): void; /** * Set specific options. @@ -4120,30 +4033,26 @@ public static function setWeekendDays($days); * * @return $this|static */ - public function settings(array $settings); + public function settings(array $settings): static; /** * Set the instance's timezone from a string or object and add/subtract the offset difference. - * - * @param DateTimeZone|string $value - * - * @return static */ - public function shiftTimezone($value); + public function shiftTimezone(DateTimeZone|string $value): static; /** * Get the month overflow global behavior (can be overridden in specific instances). * * @return bool */ - public static function shouldOverflowMonths(); + public static function shouldOverflowMonths(): bool; /** * Get the month overflow global behavior (can be overridden in specific instances). * * @return bool */ - public static function shouldOverflowYears(); + public static function shouldOverflowYears(): bool; /** * @alias diffForHumans @@ -4155,29 +4064,22 @@ public function since($other = null, $syntax = null, $short = false, $parts = 1, /** * Returns standardized singular of a given singular/plural unit name (in English). - * - * @param string $unit - * - * @return string */ public static function singularUnit(string $unit): string; + public static function sleep(int|float $seconds): void; + /** * Modify to start of current given unit. * * @example * ``` * echo Carbon::parse('2018-07-25 12:45:16.334455') - * ->startOf('month') - * ->endOf('week', Carbon::FRIDAY); + * ->startOf(Unit::Month) + * ->endOf(Unit::Week, Carbon::FRIDAY); * ``` - * - * @param string $unit - * @param array $params - * - * @return static */ - public function startOf($unit, ...$params); + public function startOf(Unit|string $unit, mixed ...$params): static; /** * Resets the date to the first day of the century and the time to 00:00:00 @@ -4222,10 +4124,8 @@ public function startOfDecade(); * ``` * echo Carbon::parse('2018-07-25 12:45:16')->startOfHour(); * ``` - * - * @return static */ - public function startOfHour(); + public function startOfHour(): static; /** * Resets the date to the first day of the millennium and the time to 00:00:00 @@ -4239,6 +4139,18 @@ public function startOfHour(); */ public function startOfMillennium(); + /** + * Modify to start of current millisecond, microseconds such as 12345 become 123000 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16.334455') + * ->startOfSecond() + * ->format('H:i:s.u'); + * ``` + */ + public function startOfMillisecond(): static; + /** * Modify to start of current minute, seconds become 0 * @@ -4246,10 +4158,8 @@ public function startOfMillennium(); * ``` * echo Carbon::parse('2018-07-25 12:45:16')->startOfMinute(); * ``` - * - * @return static */ - public function startOfMinute(); + public function startOfMinute(): static; /** * Resets the date to the first day of the month and the time to 00:00:00 @@ -4284,10 +4194,8 @@ public function startOfQuarter(); * ->startOfSecond() * ->format('H:i:s.u'); * ``` - * - * @return static */ - public function startOfSecond(); + public function startOfSecond(): static; /** * Resets the date to the first day of week (defined in $weekStartsAt) and the time to 00:00:00 @@ -4299,11 +4207,11 @@ public function startOfSecond(); * echo Carbon::parse('2018-07-25 12:45:16')->startOfWeek(Carbon::SUNDAY) . "\n"; * ``` * - * @param int $weekStartsAt optional start allow you to specify the day of week to use to start the week + * @param WeekDay|int|null $weekStartsAt optional start allow you to specify the day of week to use to start the week * * @return static */ - public function startOfWeek($weekStartsAt = null); + public function startOfWeek(WeekDay|int|null $weekStartsAt = null): static; /** * Resets the date to the first day of the year and the time to 00:00:00 @@ -4324,27 +4232,43 @@ public function startOfYear(); * @example $date->sub(15, 'days') * @example $date->sub(CarbonInterval::days(4)) * - * @param string|DateInterval|Closure|CarbonConverterInterface $unit - * @param int $value - * @param bool|null $overflow + * @param Unit|string|DateInterval|Closure|CarbonConverterInterface $unit + * @param int|float $value + * @param bool|null $overflow * * @return static */ #[ReturnTypeWillChange] - public function sub($unit, $value = 1, $overflow = null); + public function sub($unit, $value = 1, ?bool $overflow = null): static; - public function subRealUnit($unit, $value = 1); + /** + * @deprecated Prefer to use add subUTCUnit() which more accurately defines what it's doing. + * + * Subtract seconds to the instance using timestamp. Positive $value travels + * into the past while negative $value travels forward. + * + * @param string $unit + * @param int $value + * + * @return static + */ + public function subRealUnit($unit, $value = 1): static; /** - * Subtract given units to the current instance. + * Subtract seconds to the instance using timestamp. Positive $value travels + * into the past while negative $value travels forward. * - * @param string $unit - * @param int $value - * @param bool|null $overflow + * @param string $unit + * @param int $value * * @return static */ - public function subUnit($unit, $value = 1, $overflow = null); + public function subUTCUnit($unit, $value = 1): static; + + /** + * Subtract given units to the current instance. + */ + public function subUnit(Unit|string $unit, $value = 1, ?bool $overflow = null): static; /** * Subtract any unit to a new value without overflowing current other unit given. @@ -4352,10 +4276,8 @@ public function subUnit($unit, $value = 1, $overflow = null); * @param string $valueUnit unit name to modify * @param int $value amount to subtract to the input unit * @param string $overflowUnit unit name to not overflow - * - * @return static */ - public function subUnitNoOverflow($valueUnit, $value, $overflowUnit); + public function subUnitNoOverflow(string $valueUnit, int $value, string $overflowUnit): static; /** * Subtract given units or interval to the current instance. @@ -4363,40 +4285,32 @@ public function subUnitNoOverflow($valueUnit, $value, $overflowUnit); * @see sub() * * @param string|DateInterval $unit - * @param int $value + * @param int|float $value * @param bool|null $overflow * * @return static */ - public function subtract($unit, $value = 1, $overflow = null); + public function subtract($unit, $value = 1, ?bool $overflow = null): static; /** - * Get the difference in a human readable format in the current locale from current instance to an other + * Get the difference in a human-readable format in the current locale from current instance to another * instance given (or now if null given). * * @return string */ - public function timespan($other = null, $timezone = null); + public function timespan($other = null, $timezone = null): string; /** * Set the instance's timestamp. * * Timestamp input can be given as int, float or a string containing one or more numbers. - * - * @param float|int|string $unixTimestamp - * - * @return static */ - public function timestamp($unixTimestamp); + public function timestamp(string|int|float $timestamp): static; /** * @alias setTimezone - * - * @param DateTimeZone|string $value - * - * @return static */ - public function timezone($value); + public function timezone(DateTimeZone|string|int $value): static; /** * Get the difference in a human readable format in the current locale from an other @@ -4455,10 +4369,8 @@ public function to($other = null, $syntax = null, $short = false, $parts = 1, $o * ``` * var_dump(Carbon::now()->toArray()); * ``` - * - * @return array */ - public function toArray(); + public function toArray(): array; /** * Format the instance as ATOM @@ -4467,10 +4379,8 @@ public function toArray(); * ``` * echo Carbon::now()->toAtomString(); * ``` - * - * @return string */ - public function toAtomString(); + public function toAtomString(): string; /** * Format the instance as COOKIE @@ -4479,10 +4389,8 @@ public function toAtomString(); * ``` * echo Carbon::now()->toCookieString(); * ``` - * - * @return string */ - public function toCookieString(); + public function toCookieString(): string; /** * @alias toDateTime @@ -4493,10 +4401,8 @@ public function toCookieString(); * ``` * var_dump(Carbon::now()->toDate()); * ``` - * - * @return DateTime */ - public function toDate(); + public function toDate(): DateTime; /** * Format the instance as date @@ -4505,10 +4411,8 @@ public function toDate(); * ``` * echo Carbon::now()->toDateString(); * ``` - * - * @return string */ - public function toDateString(); + public function toDateString(): string; /** * Return native DateTime PHP object matching the current instance. @@ -4517,10 +4421,8 @@ public function toDateString(); * ``` * var_dump(Carbon::now()->toDateTime()); * ``` - * - * @return DateTime */ - public function toDateTime(); + public function toDateTime(): DateTime; /** * Return native toDateTimeImmutable PHP object matching the current instance. @@ -4529,10 +4431,8 @@ public function toDateTime(); * ``` * var_dump(Carbon::now()->toDateTimeImmutable()); * ``` - * - * @return DateTimeImmutable */ - public function toDateTimeImmutable(); + public function toDateTimeImmutable(): DateTimeImmutable; /** * Format the instance as date and time T-separated with no timezone @@ -4543,12 +4443,8 @@ public function toDateTimeImmutable(); * echo "\n"; * echo Carbon::now()->toDateTimeLocalString('minute'); // You can specify precision among: minute, second, millisecond and microsecond * ``` - * - * @param string $unitPrecision - * - * @return string */ - public function toDateTimeLocalString($unitPrecision = 'second'); + public function toDateTimeLocalString(string $unitPrecision = 'second'): string; /** * Format the instance as date and time @@ -4557,12 +4453,8 @@ public function toDateTimeLocalString($unitPrecision = 'second'); * ``` * echo Carbon::now()->toDateTimeString(); * ``` - * - * @param string $unitPrecision - * - * @return string */ - public function toDateTimeString($unitPrecision = 'second'); + public function toDateTimeString(string $unitPrecision = 'second'): string; /** * Format the instance with day, date and time @@ -4571,10 +4463,8 @@ public function toDateTimeString($unitPrecision = 'second'); * ``` * echo Carbon::now()->toDayDateTimeString(); * ``` - * - * @return string */ - public function toDayDateTimeString(); + public function toDayDateTimeString(): string; /** * Format the instance as a readable date @@ -4583,10 +4473,8 @@ public function toDayDateTimeString(); * ``` * echo Carbon::now()->toFormattedDateString(); * ``` - * - * @return string */ - public function toFormattedDateString(); + public function toFormattedDateString(): string; /** * Format the instance with the day, and a readable date @@ -4595,8 +4483,6 @@ public function toFormattedDateString(); * ``` * echo Carbon::now()->toFormattedDayDateString(); * ``` - * - * @return string */ public function toFormattedDayDateString(): string; @@ -4611,10 +4497,8 @@ public function toFormattedDayDateString(): string; * ``` * * @param bool $keepOffset Pass true to keep the date offset. Else forced to UTC. - * - * @return null|string */ - public function toISOString($keepOffset = false); + public function toISOString(bool $keepOffset = false): ?string; /** * Return a immutable copy of the instance. @@ -4630,10 +4514,8 @@ public function toImmutable(); * ``` * echo Carbon::now()->toIso8601String(); * ``` - * - * @return string */ - public function toIso8601String(); + public function toIso8601String(): string; /** * Convert the instance to UTC and return as Zulu ISO8601 @@ -4642,12 +4524,8 @@ public function toIso8601String(); * ``` * echo Carbon::now()->toIso8601ZuluString(); * ``` - * - * @param string $unitPrecision - * - * @return string */ - public function toIso8601ZuluString($unitPrecision = 'second'); + public function toIso8601ZuluString(string $unitPrecision = 'second'): string; /** * Return the ISO-8601 string (ex: 1977-04-22T06:00:00Z) with UTC timezone. @@ -4656,10 +4534,8 @@ public function toIso8601ZuluString($unitPrecision = 'second'); * ``` * echo Carbon::now('America/Toronto')->toJSON(); * ``` - * - * @return null|string */ - public function toJSON(); + public function toJSON(): ?string; /** * Return a mutable copy of the instance. @@ -4705,10 +4581,8 @@ public function toNow($syntax = null, $short = false, $parts = 1, $options = nul * ``` * var_dump(Carbon::now()->toObject()); * ``` - * - * @return object */ - public function toObject(); + public function toObject(): object; /** * Create a iterable CarbonPeriod object from current date to a given end date (and optional interval). @@ -4716,10 +4590,8 @@ public function toObject(); * @param \DateTimeInterface|Carbon|CarbonImmutable|int|null $end period end date or recurrences count if int * @param int|\DateInterval|string|null $interval period default interval or number of the given $unit * @param string|null $unit if specified, $interval must be an integer - * - * @return CarbonPeriod */ - public function toPeriod($end = null, $interval = null, $unit = null); + public function toPeriod($end = null, $interval = null, $unit = null): CarbonPeriod; /** * Format the instance as RFC1036 @@ -4728,10 +4600,8 @@ public function toPeriod($end = null, $interval = null, $unit = null); * ``` * echo Carbon::now()->toRfc1036String(); * ``` - * - * @return string */ - public function toRfc1036String(); + public function toRfc1036String(): string; /** * Format the instance as RFC1123 @@ -4740,10 +4610,8 @@ public function toRfc1036String(); * ``` * echo Carbon::now()->toRfc1123String(); * ``` - * - * @return string */ - public function toRfc1123String(); + public function toRfc1123String(): string; /** * Format the instance as RFC2822 @@ -4752,25 +4620,19 @@ public function toRfc1123String(); * ``` * echo Carbon::now()->toRfc2822String(); * ``` - * - * @return string */ - public function toRfc2822String(); + public function toRfc2822String(): string; /** - * Format the instance as RFC3339 - * - * @param bool $extended + * Format the instance as RFC3339. * * @example * ``` * echo Carbon::now()->toRfc3339String() . "\n"; * echo Carbon::now()->toRfc3339String(true) . "\n"; * ``` - * - * @return string */ - public function toRfc3339String($extended = false); + public function toRfc3339String(bool $extended = false): string; /** * Format the instance as RFC7231 @@ -4779,10 +4641,8 @@ public function toRfc3339String($extended = false); * ``` * echo Carbon::now()->toRfc7231String(); * ``` - * - * @return string */ - public function toRfc7231String(); + public function toRfc7231String(): string; /** * Format the instance as RFC822 @@ -4791,10 +4651,8 @@ public function toRfc7231String(); * ``` * echo Carbon::now()->toRfc822String(); * ``` - * - * @return string */ - public function toRfc822String(); + public function toRfc822String(): string; /** * Format the instance as RFC850 @@ -4803,10 +4661,8 @@ public function toRfc822String(); * ``` * echo Carbon::now()->toRfc850String(); * ``` - * - * @return string */ - public function toRfc850String(); + public function toRfc850String(): string; /** * Format the instance as RSS @@ -4815,22 +4671,18 @@ public function toRfc850String(); * ``` * echo Carbon::now()->toRssString(); * ``` - * - * @return string */ - public function toRssString(); + public function toRssString(): string; /** - * Returns english human readable complete date string. + * Returns english human-readable complete date string. * * @example * ``` * echo Carbon::now()->toString(); * ``` - * - * @return string */ - public function toString(); + public function toString(): string; /** * Format the instance as time @@ -4839,12 +4691,8 @@ public function toString(); * ``` * echo Carbon::now()->toTimeString(); * ``` - * - * @param string $unitPrecision - * - * @return string */ - public function toTimeString($unitPrecision = 'second'); + public function toTimeString(string $unitPrecision = 'second'): string; /** * Format the instance as W3C @@ -4853,41 +4701,31 @@ public function toTimeString($unitPrecision = 'second'); * ``` * echo Carbon::now()->toW3cString(); * ``` - * - * @return string */ - public function toW3cString(); + public function toW3cString(): string; /** * Create a Carbon instance for today. - * - * @param DateTimeZone|string|null $tz - * - * @return static */ - public static function today($tz = null); + public static function today(DateTimeZone|string|int|null $timezone = null): static; /** * Create a Carbon instance for tomorrow. - * - * @param DateTimeZone|string|null $tz - * - * @return static */ - public static function tomorrow($tz = null); + public static function tomorrow(DateTimeZone|string|int|null $timezone = null): static; /** * Translate using translation string or callback available. * - * @param string $key - * @param array $parameters - * @param string|int|float|null $number - * @param \Symfony\Component\Translation\TranslatorInterface|null $translator - * @param bool $altNumbers + * @param string $key key to find + * @param array $parameters replacement parameters + * @param string|int|float|null $number number if plural + * @param TranslatorInterface|null $translator an optional translator to use + * @param bool $altNumbers pass true to use alternative numbers * * @return string */ - public function translate(string $key, array $parameters = [], $number = null, ?TranslatorInterface $translator = null, bool $altNumbers = false): string; + public function translate(string $key, array $parameters = [], string|int|float|null $number = null, ?TranslatorInterface $translator = null, bool $altNumbers = false): string; /** * Returns the alternative number for a given integer if available in the current locale. @@ -4914,7 +4752,7 @@ public function translateNumber(int $number): string; * * @return string */ - public static function translateTimeString($timeString, $from = null, $to = null, $mode = self::TRANSLATE_ALL); + public static function translateTimeString(string $timeString, ?string $from = null, ?string $to = null, int $mode = self::TRANSLATE_ALL): string; /** * Translate a time string from the current locale (`$date->locale()`) to an other. @@ -4924,15 +4762,15 @@ public static function translateTimeString($timeString, $from = null, $to = null * * @return string */ - public function translateTimeStringTo($timeString, $to = null); + public function translateTimeStringTo(string $timeString, ?string $to = null): string; /** * Translate using translation string or callback available. * - * @param \Symfony\Component\Translation\TranslatorInterface $translator - * @param string $key - * @param array $parameters - * @param null $number + * @param TranslatorInterface $translator an optional translator to use + * @param string $key key to find + * @param array $parameters replacement parameters + * @param int|float|null $number number if plural * * @return string */ @@ -4941,21 +4779,13 @@ public static function translateWith(TranslatorInterface $translator, string $ke /** * Format as ->format() do (using date replacements patterns from https://php.net/manual/en/function.date.php) * but translate words whenever possible (months, day names, etc.) using the current locale. - * - * @param string $format - * - * @return string */ public function translatedFormat(string $format): string; /** * Set the timezone or returns the timezone name if no arguments passed. - * - * @param DateTimeZone|string $value - * - * @return static|string */ - public function tz($value = null); + public function tz(DateTimeZone|string|int|null $value = null): static|string; /** * @alias getTimestamp @@ -4964,7 +4794,7 @@ public function tz($value = null); * * @return int */ - public function unix(); + public function unix(): int; /** * @alias to @@ -5015,7 +4845,7 @@ public function until($other = null, $syntax = null, $short = false, $parts = 1, * * @return void */ - public static function useMonthsOverflow($monthsOverflow = true); + public static function useMonthsOverflow(bool $monthsOverflow = true): void; /** * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. @@ -5026,7 +4856,7 @@ public static function useMonthsOverflow($monthsOverflow = true); * * @param bool $strictModeEnabled */ - public static function useStrictMode($strictModeEnabled = true); + public static function useStrictMode(bool $strictModeEnabled = true): void; /** * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. @@ -5041,30 +4871,24 @@ public static function useStrictMode($strictModeEnabled = true); * * @return void */ - public static function useYearsOverflow($yearsOverflow = true); + public static function useYearsOverflow(bool $yearsOverflow = true): void; /** * Set the instance's timezone to UTC. - * - * @return static */ - public function utc(); + public function utc(): static; /** * Returns the minutes offset to UTC if no arguments passed, else set the timezone with given minutes shift passed. - * - * @param int|null $minuteOffset - * - * @return int|static */ - public function utcOffset(?int $minuteOffset = null); + public function utcOffset(?int $minuteOffset = null): static|int; /** * Returns the milliseconds timestamps used amongst other by Date javascript objects. * * @return float */ - public function valueOf(); + public function valueOf(): float; /** * Get/set the week number using given first day of week and first @@ -5095,11 +4919,9 @@ public function weekYear($year = null, $dayOfWeek = null, $dayOfYear = null); /** * Get/set the weekday from 0 (Sunday) to 6 (Saturday). * - * @param int|null $value new value for weekday if using as setter. - * - * @return static|int + * @param WeekDay|int|null $value new value for weekday if using as setter. */ - public function weekday($value = null); + public function weekday(WeekDay|int|null $value = null): static|int; /** * Get the number of weeks of the current week-year using given first day of week and first @@ -5127,16 +4949,12 @@ public function weeksInYear($dayOfWeek = null, $dayOfYear = null); * * @return T */ - public static function withTestNow($testNow, $callback); + public static function withTestNow(mixed $testNow, callable $callback): mixed; /** * Create a Carbon instance for yesterday. - * - * @param DateTimeZone|string|null $tz - * - * @return static */ - public static function yesterday($tz = null); + public static function yesterday(DateTimeZone|string|int|null $timezone = null): static; // } diff --git a/vendor/nesbot/carbon/src/Carbon/CarbonInterval.php b/vendor/nesbot/carbon/src/Carbon/CarbonInterval.php index 8437c545e..a582ddb10 100644 --- a/vendor/nesbot/carbon/src/Carbon/CarbonInterval.php +++ b/vendor/nesbot/carbon/src/Carbon/CarbonInterval.php @@ -1,5 +1,7 @@ seconds(34)->microseconds(567_890)->seconds = 34) + * @property int $milliseconds Milliseconds component of the current interval. (CarbonInterval::seconds(34)->microseconds(567_890)->milliseconds = 567) + * @property int $microseconds Microseconds component of the current interval. (CarbonInterval::seconds(34)->microseconds(567_890)->microseconds = 567_890) * @property int $microExcludeMilli Remaining microseconds without the milliseconds. * @property int $dayzExcludeWeeks Total days remaining in the final week of the current instance (days % 7). * @property int $daysExcludeWeeks alias of dayzExcludeWeeks - * @property-read float $totalYears Number of years equivalent to the interval. - * @property-read float $totalMonths Number of months equivalent to the interval. - * @property-read float $totalWeeks Number of weeks equivalent to the interval. - * @property-read float $totalDays Number of days equivalent to the interval. + * @property-read float $totalYears Number of years equivalent to the interval. (For P1Y6M, the value will be 1.5) + * @property-read float $totalMonths Number of months equivalent to the interval. (For P1Y6M10D, the value will be ~12.357) + * @property-read float $totalWeeks Number of weeks equivalent to the interval. (For P6M17DT20H, the value will be ~26.548) + * @property-read float $totalDays Number of days equivalent to the interval. (For P17DT20H, the value will be ~17.833) * @property-read float $totalDayz Alias for totalDays. - * @property-read float $totalHours Number of hours equivalent to the interval. - * @property-read float $totalMinutes Number of minutes equivalent to the interval. - * @property-read float $totalSeconds Number of seconds equivalent to the interval. - * @property-read float $totalMilliseconds Number of milliseconds equivalent to the interval. - * @property-read float $totalMicroseconds Number of microseconds equivalent to the interval. + * @property-read float $totalHours Number of hours equivalent to the interval. (For P1DT20H5M, the value will be ~44.083) + * @property-read float $totalMinutes Number of minutes equivalent to the interval. (For PT20H5M30S, the value will be 1205.5) + * @property-read float $totalSeconds Number of seconds equivalent to the interval. (CarbonInterval::minutes(2)->seconds(34)->microseconds(567_890)->totalSeconds = 154.567_890) + * @property-read float $totalMilliseconds Number of milliseconds equivalent to the interval. (CarbonInterval::seconds(34)->microseconds(567_890)->totalMilliseconds = 34567.890) + * @property-read float $totalMicroseconds Number of microseconds equivalent to the interval. (CarbonInterval::seconds(34)->microseconds(567_890)->totalMicroseconds = 34567890) * @property-read string $locale locale of the current instance * * @method static CarbonInterval years($years = 1) Create instance specifying a number of years or modify the number of years if called on an instance. @@ -188,6 +193,7 @@ */ class CarbonInterval extends DateInterval implements CarbonConverterInterface { + use LocalFactory; use IntervalRounding; use IntervalStep; use MagicParameter; @@ -197,6 +203,16 @@ class CarbonInterval extends DateInterval implements CarbonConverterInterface use Options; use ToStringFormat; + /** + * Unlimited parts for forHumans() method. + * + * INF constant can be used instead. + */ + public const NO_LIMIT = -1; + + public const POSITIVE = 1; + public const NEGATIVE = -1; + /** * Interval spec period designators */ @@ -209,22 +225,22 @@ class CarbonInterval extends DateInterval implements CarbonConverterInterface public const PERIOD_MINUTES = 'M'; public const PERIOD_SECONDS = 'S'; - /** - * A translator to ... er ... translate stuff - * - * @var \Symfony\Component\Translation\TranslatorInterface - */ - protected static $translator; + public const SPECIAL_TRANSLATIONS = [ + 1 => [ + 'option' => CarbonInterface::ONE_DAY_WORDS, + 'future' => 'diff_tomorrow', + 'past' => 'diff_yesterday', + ], + 2 => [ + 'option' => CarbonInterface::TWO_DAY_WORDS, + 'future' => 'diff_after_tomorrow', + 'past' => 'diff_before_yesterday', + ], + ]; - /** - * @var array|null - */ - protected static $cascadeFactors; + protected static ?array $cascadeFactors = null; - /** - * @var array - */ - protected static $formats = [ + protected static array $formats = [ 'y' => 'y', 'Y' => 'y', 'o' => 'y', @@ -244,56 +260,93 @@ class CarbonInterval extends DateInterval implements CarbonConverterInterface 'v' => 'milli', ]; + private static ?array $flipCascadeFactors = null; + + private static bool $floatSettersEnabled = false; + /** - * @var array|null + * The registered macros. */ - private static $flipCascadeFactors; + protected static array $macros = []; /** - * @var bool + * Timezone handler for settings() method. */ - private static $floatSettersEnabled = false; + protected DateTimeZone|string|int|null $timezoneSetting = null; /** - * The registered macros. - * - * @var array + * The input used to create the interval. */ - protected static $macros = []; + protected mixed $originalInput = null; /** - * Timezone handler for settings() method. - * - * @var mixed + * Start date if interval was created from a difference between 2 dates. + */ + protected ?CarbonInterface $startDate = null; + + /** + * End date if interval was created from a difference between 2 dates. + */ + protected ?CarbonInterface $endDate = null; + + /** + * End date if interval was created from a difference between 2 dates. */ - protected $tzName; + protected ?DateInterval $rawInterval = null; + + /** + * Flag if the interval was made from a diff with absolute flag on. + */ + protected bool $absolute = false; + + protected ?array $initialValues = null; /** * Set the instance's timezone from a string or object. - * - * @param \DateTimeZone|string $tzName - * - * @return static */ - public function setTimezone($tzName) + public function setTimezone(DateTimeZone|string|int $timezone): static { - $this->tzName = $tzName; + $this->timezoneSetting = $timezone; + $this->checkStartAndEnd(); + + if ($this->startDate) { + $this->startDate = $this->startDate + ->avoidMutation() + ->setTimezone($timezone); + $this->rawInterval = null; + } + + if ($this->endDate) { + $this->endDate = $this->endDate + ->avoidMutation() + ->setTimezone($timezone); + $this->rawInterval = null; + } return $this; } /** - * @internal - * * Set the instance's timezone from a string or object and add/subtract the offset difference. - * - * @param \DateTimeZone|string $tzName - * - * @return static */ - public function shiftTimezone($tzName) + public function shiftTimezone(DateTimeZone|string|int $timezone): static { - $this->tzName = $tzName; + $this->timezoneSetting = $timezone; + $this->checkStartAndEnd(); + + if ($this->startDate) { + $this->startDate = $this->startDate + ->avoidMutation() + ->shiftTimezone($timezone); + $this->rawInterval = null; + } + + if ($this->endDate) { + $this->endDate = $this->endDate + ->avoidMutation() + ->shiftTimezone($timezone); + $this->rawInterval = null; + } return $this; } @@ -302,10 +355,8 @@ public function shiftTimezone($tzName) * Mapping of units and factors for cascading. * * Should only be modified by changing the factors or referenced constants. - * - * @return array */ - public static function getCascadeFactors() + public static function getCascadeFactors(): array { return static::$cascadeFactors ?: static::getDefaultCascadeFactors(); } @@ -313,37 +364,17 @@ public static function getCascadeFactors() protected static function getDefaultCascadeFactors(): array { return [ - 'milliseconds' => [Carbon::MICROSECONDS_PER_MILLISECOND, 'microseconds'], - 'seconds' => [Carbon::MILLISECONDS_PER_SECOND, 'milliseconds'], - 'minutes' => [Carbon::SECONDS_PER_MINUTE, 'seconds'], - 'hours' => [Carbon::MINUTES_PER_HOUR, 'minutes'], - 'dayz' => [Carbon::HOURS_PER_DAY, 'hours'], - 'weeks' => [Carbon::DAYS_PER_WEEK, 'dayz'], - 'months' => [Carbon::WEEKS_PER_MONTH, 'weeks'], - 'years' => [Carbon::MONTHS_PER_YEAR, 'months'], + 'milliseconds' => [CarbonInterface::MICROSECONDS_PER_MILLISECOND, 'microseconds'], + 'seconds' => [CarbonInterface::MILLISECONDS_PER_SECOND, 'milliseconds'], + 'minutes' => [CarbonInterface::SECONDS_PER_MINUTE, 'seconds'], + 'hours' => [CarbonInterface::MINUTES_PER_HOUR, 'minutes'], + 'dayz' => [CarbonInterface::HOURS_PER_DAY, 'hours'], + 'weeks' => [CarbonInterface::DAYS_PER_WEEK, 'dayz'], + 'months' => [CarbonInterface::WEEKS_PER_MONTH, 'weeks'], + 'years' => [CarbonInterface::MONTHS_PER_YEAR, 'months'], ]; } - private static function standardizeUnit($unit) - { - $unit = rtrim($unit, 'sz').'s'; - - return $unit === 'days' ? 'dayz' : $unit; - } - - private static function getFlipCascadeFactors() - { - if (!self::$flipCascadeFactors) { - self::$flipCascadeFactors = []; - - foreach (static::getCascadeFactors() as $to => [$factor, $from]) { - self::$flipCascadeFactors[self::standardizeUnit($from)] = [self::standardizeUnit($to), $factor]; - } - } - - return self::$flipCascadeFactors; - } - /** * Set default cascading factors for ->cascade() method. * @@ -386,8 +417,10 @@ public static function enableFloatSetters(bool $floatSettersEnabled = true): voi * * @throws Exception when the interval_spec (passed as $years) cannot be parsed as an interval. */ - public function __construct($years = 1, $months = null, $weeks = null, $days = null, $hours = null, $minutes = null, $seconds = null, $microseconds = null) + public function __construct($years = null, $months = null, $weeks = null, $days = null, $hours = null, $minutes = null, $seconds = null, $microseconds = null) { + $this->originalInput = \func_num_args() === 1 ? $years : \func_get_args(); + if ($years instanceof Closure) { $this->step = $years; $years = null; @@ -464,6 +497,11 @@ public function __construct($years = 1, $months = null, $weeks = null, $days = n $this->assertSafeForInteger('minute', $minutes); $seconds = (float) ($match['second'] ?? 0); $this->assertSafeForInteger('second', $seconds); + $microseconds = (int) str_pad( + substr(explode('.', $match['second'] ?? '0.0')[1] ?? '0', 0, 6), + 6, + '0', + ); } $totalDays = (($weeks * static::getDaysPerWeek()) + $days); @@ -475,6 +513,10 @@ public function __construct($years = 1, $months = null, $weeks = null, $days = n $this->h = (int) $hours; $this->i = (int) $minutes; $this->s = (int) $seconds; + $secondFloatPart = (float) ($microseconds / CarbonInterface::MICROSECONDS_PER_SECOND); + $this->f = $secondFloatPart; + $intervalMicroseconds = (int) ($this->f * CarbonInterface::MICROSECONDS_PER_SECOND); + $intervalSeconds = $seconds - $secondFloatPart; if ( ((float) $this->y) !== $years || @@ -482,7 +524,8 @@ public function __construct($years = 1, $months = null, $weeks = null, $days = n ((float) $this->d) !== $totalDays || ((float) $this->h) !== $hours || ((float) $this->i) !== $minutes || - ((float) $this->s) !== $seconds + ((float) $this->s) !== $intervalSeconds || + $intervalMicroseconds !== ((int) $microseconds) ) { $this->add(static::fromString( ($years - $this->y).' years '. @@ -490,7 +533,8 @@ public function __construct($years = 1, $months = null, $weeks = null, $days = n ($totalDays - $this->d).' days '. ($hours - $this->h).' hours '. ($minutes - $this->i).' minutes '. - ($seconds - $this->s).' seconds ' + number_format($intervalSeconds - $this->s, 6, '.', '').' seconds '. + ($microseconds - $intervalMicroseconds).' microseconds ', )); } } catch (Throwable $secondException) { @@ -499,7 +543,13 @@ public function __construct($years = 1, $months = null, $weeks = null, $days = n } if ($microseconds !== null) { - $this->f = $microseconds / Carbon::MICROSECONDS_PER_SECOND; + $this->f = $microseconds / CarbonInterface::MICROSECONDS_PER_SECOND; + } + + foreach (['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds'] as $unit) { + if ($$unit < 0) { + $this->set($unit, $$unit); + } } } @@ -640,7 +690,7 @@ public static function getMicrosecondsPerMillisecond() * * @return static */ - public static function create($years = 1, $months = null, $weeks = null, $days = null, $hours = null, $minutes = null, $seconds = null, $microseconds = null) + public static function create($years = null, $months = null, $weeks = null, $days = null, $hours = null, $minutes = null, $seconds = null, $microseconds = null) { return new static($years, $months, $weeks, $days, $hours, $minutes, $seconds, $microseconds); } @@ -660,7 +710,7 @@ public static function create($years = 1, $months = null, $weeks = null, $days = * * @return static */ - public static function createFromFormat(string $format, ?string $interval) + public static function createFromFormat(string $format, ?string $interval): static { $instance = new static(0); $length = mb_strlen($format); @@ -672,7 +722,7 @@ public static function createFromFormat(string $format, ?string $interval) $interval = implode($match[1], $interval); } - $interval = $interval ?? ''; + $interval ??= ''; for ($index = 0; $index < $length; $index++) { $expected = mb_substr($format, $index, 1); @@ -685,7 +735,7 @@ public static function createFromFormat(string $format, ?string $interval) } $interval = mb_substr($interval, mb_strlen($match[0])); - $instance->$unit += (int) ($match[0]); + self::incrementUnit($instance, $unit, (int) ($match[0])); continue; } @@ -695,7 +745,7 @@ public static function createFromFormat(string $format, ?string $interval) "'$expected'", $nextCharacter, 'Allowed substitutes for interval formats are '.implode(', ', array_keys(static::$formats))."\n". - 'See https://php.net/manual/en/function.date.php for their meaning' + 'See https://php.net/manual/en/function.date.php for their meaning', ); } @@ -705,19 +755,69 @@ public static function createFromFormat(string $format, ?string $interval) if ($interval !== '') { throw new ParseErrorException( 'end of string', - $interval + $interval, ); } return $instance; } + /** + * Return the original source used to create the current interval. + * + * @return array|int|string|DateInterval|mixed|null + */ + public function original() + { + return $this->originalInput; + } + + /** + * Return the start date if interval was created from a difference between 2 dates. + * + * @return CarbonInterface|null + */ + public function start(): ?CarbonInterface + { + $this->checkStartAndEnd(); + + return $this->startDate; + } + + /** + * Return the end date if interval was created from a difference between 2 dates. + * + * @return CarbonInterface|null + */ + public function end(): ?CarbonInterface + { + $this->checkStartAndEnd(); + + return $this->endDate; + } + + /** + * Get rid of the original input, start date and end date that may be kept in memory. + * + * @return $this + */ + public function optimize(): static + { + $this->originalInput = null; + $this->startDate = null; + $this->endDate = null; + $this->rawInterval = null; + $this->absolute = false; + + return $this; + } + /** * Get a copy of the instance. * * @return static */ - public function copy() + public function copy(): static { $date = new static(0); $date->copyProperties($this); @@ -731,7 +831,7 @@ public function copy() * * @return static */ - public function clone() + public function clone(): static { return $this->copy(); } @@ -745,9 +845,9 @@ public function clone() * @param string $method magic method name called * @param array $parameters parameters list * - * @return static|null + * @return static|mixed|null */ - public static function __callStatic($method, $parameters) + public static function __callStatic(string $method, array $parameters) { try { $interval = new static(0); @@ -794,7 +894,7 @@ public static function __set_state($dump) * * @return static */ - protected static function this() + protected static function this(): static { return end(static::$macroContextStack) ?: new static(0); } @@ -823,12 +923,14 @@ protected static function this() * * @param string $intervalDefinition * + * @throws InvalidIntervalException + * * @return static */ - public static function fromString($intervalDefinition) + public static function fromString(string $intervalDefinition): static { if (empty($intervalDefinition)) { - return new static(0); + return self::withOriginal(new static(0), $intervalDefinition); } $years = 0; @@ -841,7 +943,7 @@ public static function fromString($intervalDefinition) $milliseconds = 0; $microseconds = 0; - $pattern = '/(\d+(?:\.\d+)?)\h*([^\d\h]*)/i'; + $pattern = '/(-?\d+(?:\.\d+)?)\h*([^\d\h]*)/i'; preg_match_all($pattern, $intervalDefinition, $parts, PREG_SET_ORDER); while ([$part, $value, $unit] = array_shift($parts)) { @@ -980,12 +1082,15 @@ public static function fromString($intervalDefinition) default: throw new InvalidIntervalException( - sprintf('Invalid part %s in definition %s', $part, $intervalDefinition) + "Invalid part $part in definition $intervalDefinition", ); } } - return new static($years, $months, $weeks, $days, $hours, $minutes, $seconds, $milliseconds * Carbon::MICROSECONDS_PER_MILLISECOND + $microseconds); + return self::withOriginal( + new static($years, $months, $weeks, $days, $hours, $minutes, $seconds, $milliseconds * Carbon::MICROSECONDS_PER_MILLISECOND + $microseconds), + $intervalDefinition, + ); } /** @@ -996,59 +1101,75 @@ public static function fromString($intervalDefinition) * * @return static */ - public static function parseFromLocale($interval, $locale = null) + public static function parseFromLocale(string $interval, ?string $locale = null): static { - return static::fromString(Carbon::translateTimeString($interval, $locale ?: static::getLocale(), 'en')); + return static::fromString(Carbon::translateTimeString($interval, $locale ?: static::getLocale(), CarbonInterface::DEFAULT_LOCALE)); } - private static function castIntervalToClass(DateInterval $interval, string $className, array $skip = []) + /** + * Create an interval from the difference between 2 dates. + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $start + * @param \Carbon\Carbon|\DateTimeInterface|mixed $end + * + * @return static + */ + public static function diff($start, $end = null, bool $absolute = false, array $skip = []): static { - $mainClass = DateInterval::class; - - if (!is_a($className, $mainClass, true)) { - throw new InvalidCastException("$className is not a sub-class of $mainClass."); - } - - $microseconds = $interval->f; - $instance = new $className(static::getDateIntervalSpec($interval, false, $skip)); - - if ($microseconds) { - $instance->f = $microseconds; - } - - if ($interval instanceof self && is_a($className, self::class, true)) { - self::copyStep($interval, $instance); - } + $start = $start instanceof CarbonInterface ? $start : Carbon::make($start); + $end = $end instanceof CarbonInterface ? $end : Carbon::make($end); + $rawInterval = $start->diffAsDateInterval($end, $absolute); + $interval = static::instance($rawInterval, $skip); - self::copyNegativeUnits($interval, $instance); + $interval->absolute = $absolute; + $interval->rawInterval = $rawInterval; + $interval->startDate = $start; + $interval->endDate = $end; + $interval->initialValues = $interval->getInnerValues(); - return $instance; + return $interval; } - private static function copyNegativeUnits(DateInterval $from, DateInterval $to): void + /** + * Invert the interval if it's inverted. + * + * @param bool $absolute do nothing if set to false + * + * @return $this + */ + public function abs(bool $absolute = false): static { - $to->invert = $from->invert; - - foreach (['y', 'm', 'd', 'h', 'i', 's'] as $unit) { - if ($from->$unit < 0) { - $to->$unit *= -1; - } + if ($absolute && $this->invert) { + $this->invert(); } + + return $this; } - private static function copyStep(self $from, self $to): void + /** + * @alias abs + * + * Invert the interval if it's inverted. + * + * @param bool $absolute do nothing if set to false + * + * @return $this + */ + public function absolute(bool $absolute = true): static { - $to->setStep($from->getStep()); + return $this->abs($absolute); } /** * Cast the current instance into the given class. * - * @param string $className The $className::instance() method will be called to cast the current object. + * @template T of DateInterval * - * @return DateInterval + * @psalm-param class-string $className The $className::instance() method will be called to cast the current object. + * + * @return T */ - public function cast(string $className) + public function cast(string $className): mixed { return self::castIntervalToClass($this, $className); } @@ -1065,7 +1186,7 @@ public function cast(string $className) * * @return static */ - public static function instance(DateInterval $interval, array $skip = [], bool $skipCopy = false) + public static function instance(DateInterval $interval, array $skip = [], bool $skipCopy = false): static { if ($skipCopy && $interval instanceof static) { return $interval; @@ -1080,18 +1201,26 @@ public static function instance(DateInterval $interval, array $skip = [], bool $ * Always return a new instance. Parse only strings and only these likely to be intervals (skip dates * and recurrences). Throw an exception for invalid format, but otherwise return null. * - * @param mixed|int|DateInterval|string|Closure|null $interval interval or number of the given $unit - * @param string|null $unit if specified, $interval must be an integer - * @param bool $skipCopy set to true to return the passed object - * (without copying it) if it's already of the - * current class + * @param mixed|int|DateInterval|string|Closure|Unit|null $interval interval or number of the given $unit + * @param Unit|string|null $unit if specified, $interval must be an integer + * @param bool $skipCopy set to true to return the passed object + * (without copying it) if it's already of the + * current class * * @return static|null */ - public static function make($interval, $unit = null, bool $skipCopy = false) + public static function make($interval, $unit = null, bool $skipCopy = false): ?self { + if ($interval instanceof Unit) { + $interval = $interval->interval(); + } + + if ($unit instanceof Unit) { + $unit = $unit->value; + } + if ($unit) { - $interval = "$interval ".Carbon::pluralUnit($unit); + $interval = "$interval $unit"; } if ($interval instanceof DateInterval) { @@ -1099,7 +1228,7 @@ public static function make($interval, $unit = null, bool $skipCopy = false) } if ($interval instanceof Closure) { - return new static($interval); + return self::withOriginal(new static($interval), $interval); } if (!\is_string($interval)) { @@ -1109,7 +1238,7 @@ public static function make($interval, $unit = null, bool $skipCopy = false) return static::makeFromString($interval); } - protected static function makeFromString(string $interval) + protected static function makeFromString(string $interval): ?self { $interval = preg_replace('/\s+/', ' ', trim($interval)); @@ -1117,23 +1246,16 @@ protected static function makeFromString(string $interval) return new static($interval); } - if (preg_match('/^(?:\h*\d+(?:\.\d+)?\h*[a-z]+)+$/i', $interval)) { + if (preg_match('/^(?:\h*-?\d+(?:\.\d+)?\h*[a-z]+)+$/i', $interval)) { return static::fromString($interval); } - // @codeCoverageIgnoreStart - try { - /** @var static $interval */ - $interval = static::createFromDateString($interval); - } catch (DateMalformedIntervalStringException $e) { - return null; - } - // @codeCoverageIgnoreEnd + $intervalInstance = static::createFromDateString($interval); - return !$interval || $interval->isEmpty() ? null : $interval; + return $intervalInstance->isEmpty() ? null : $intervalInstance; } - protected function resolveInterval($interval) + protected function resolveInterval($interval): ?self { if (!($interval instanceof self)) { return self::make($interval); @@ -1145,25 +1267,37 @@ protected function resolveInterval($interval) /** * Sets up a DateInterval from the relative parts of the string. * - * @param string $time + * @param string $datetime * * @return static * * @link https://php.net/manual/en/dateinterval.createfromdatestring.php */ - #[ReturnTypeWillChange] - public static function createFromDateString($time) + public static function createFromDateString(string $datetime): static { - $interval = @parent::createFromDateString(strtr($time, [ + $string = strtr($datetime, [ ',' => ' ', ' and ' => ' ', - ])); + ]); + $previousException = null; - if ($interval instanceof DateInterval) { + try { + $interval = parent::createFromDateString($string); + } catch (Throwable $exception) { + $interval = null; + $previousException = $exception; + } + + $interval ?: throw new InvalidFormatException( + 'Could not create interval from: '.var_export($datetime, true), + previous: $previousException, + ); + + if (!($interval instanceof static)) { $interval = static::instance($interval); } - return $interval; + return self::withOriginal($interval, $datetime); } /////////////////////////////////////////////////////////////////// @@ -1172,74 +1306,46 @@ public static function createFromDateString($time) /** * Get a part of the CarbonInterval object. - * - * @param string $name - * - * @throws UnknownGetterException - * - * @return int|float|string */ - public function get($name) + public function get(Unit|string $name): int|float|string|null { + $name = Unit::toName($name); + if (str_starts_with($name, 'total')) { return $this->total(substr($name, 5)); } - switch ($name) { - case 'years': - return $this->y; - - case 'months': - return $this->m; - - case 'dayz': - return $this->d; - - case 'hours': - return $this->h; - - case 'minutes': - return $this->i; - - case 'seconds': - return $this->s; - - case 'milli': - case 'milliseconds': - return (int) (round($this->f * Carbon::MICROSECONDS_PER_SECOND) / Carbon::MICROSECONDS_PER_MILLISECOND); - - case 'micro': - case 'microseconds': - return (int) round($this->f * Carbon::MICROSECONDS_PER_SECOND); - - case 'microExcludeMilli': - return (int) round($this->f * Carbon::MICROSECONDS_PER_SECOND) % Carbon::MICROSECONDS_PER_MILLISECOND; - - case 'weeks': - return (int) ($this->d / (int) static::getDaysPerWeek()); - - case 'daysExcludeWeeks': - case 'dayzExcludeWeeks': - return $this->d % (int) static::getDaysPerWeek(); - - case 'locale': - return $this->getTranslatorLocale(); - - default: - throw new UnknownGetterException($name); - } + $resolvedUnit = Carbon::singularUnit(rtrim($name, 'z')); + + return match ($resolvedUnit) { + 'tzname', 'tz_name' => match (true) { + ($this->timezoneSetting === null) => null, + \is_string($this->timezoneSetting) => $this->timezoneSetting, + ($this->timezoneSetting instanceof DateTimeZone) => $this->timezoneSetting->getName(), + default => CarbonTimeZone::instance($this->timezoneSetting)->getName(), + }, + 'year' => $this->y, + 'month' => $this->m, + 'day' => $this->d, + 'hour' => $this->h, + 'minute' => $this->i, + 'second' => $this->s, + 'milli', 'millisecond' => (int) (round($this->f * Carbon::MICROSECONDS_PER_SECOND) / + Carbon::MICROSECONDS_PER_MILLISECOND), + 'micro', 'microsecond' => (int) round($this->f * Carbon::MICROSECONDS_PER_SECOND), + 'microexcludemilli' => (int) round($this->f * Carbon::MICROSECONDS_PER_SECOND) % + Carbon::MICROSECONDS_PER_MILLISECOND, + 'week' => (int) ($this->d / (int) static::getDaysPerWeek()), + 'daysexcludeweek', 'dayzexcludeweek' => $this->d % (int) static::getDaysPerWeek(), + 'locale' => $this->getTranslatorLocale(), + default => throw new UnknownGetterException($name, previous: new UnknownGetterException($resolvedUnit)), + }; } /** * Get a part of the CarbonInterval object. - * - * @param string $name - * - * @throws UnknownGetterException - * - * @return int|float|string */ - public function __get($name) + public function __get(string $name): int|float|string|null { return $this->get($name); } @@ -1247,19 +1353,19 @@ public function __get($name) /** * Set a part of the CarbonInterval object. * - * @param string|array $name - * @param int $value + * @param Unit|string|array $name + * @param int $value * * @throws UnknownSetterException * * @return $this */ - public function set($name, $value = null) + public function set($name, $value = null): static { $properties = \is_array($name) ? $name : [$name => $value]; foreach ($properties as $key => $value) { - switch (Carbon::singularUnit(rtrim($key, 'z'))) { + switch (Carbon::singularUnit($key instanceof Unit ? $key->value : rtrim((string) $key, 'z'))) { case 'year': $this->checkIntegerValue($key, $value); $this->y = $value; @@ -1284,6 +1390,10 @@ public function set($name, $value = null) break; case 'day': + if ($value === false) { + break; + } + $this->checkIntegerValue($key, $value); $this->d = $value; $this->handleDecimalPart('day', $value, $this->d); @@ -1334,6 +1444,10 @@ public function set($name, $value = null) break; default: + if (str_starts_with($key, ' * ')) { + return $this->setSetting(substr($key, 3), $value); + } + if ($this->localStrictModeEnabled ?? Carbon::isStrictModeEnabled()) { throw new UnknownSetterException($key); } @@ -1353,7 +1467,7 @@ public function set($name, $value = null) * * @throws UnknownSetterException */ - public function __set($name, $value) + public function __set(string $name, $value) { $this->set($name, $value); } @@ -1366,7 +1480,7 @@ public function __set($name, $value) * * @return static */ - public function weeksAndDays($weeks, $days) + public function weeksAndDays(int $weeks, int $days): static { $this->dayz = ($weeks * static::getDaysPerWeek()) + $days; @@ -1378,7 +1492,7 @@ public function weeksAndDays($weeks, $days) * * @return bool */ - public function isEmpty() + public function isEmpty(): bool { return $this->years === 0 && $this->months === 0 && @@ -1393,6 +1507,8 @@ public function isEmpty() /** * Register a custom macro. * + * Pass null macro to remove it. + * * @example * ``` * CarbonInterval::macro('twice', function () { @@ -1401,12 +1517,9 @@ public function isEmpty() * echo CarbonInterval::hours(2)->twice(); * ``` * - * @param string $name - * @param object|callable $macro - * - * @return void + * @param-closure-this static $macro */ - public static function macro($name, $macro) + public static function macro(string $name, ?callable $macro): void { static::$macros[$name] = $macro; } @@ -1444,7 +1557,7 @@ public static function macro($name, $macro) * * @return void */ - public static function mixin($mixin) + public static function mixin($mixin): void { static::baseMixin($mixin); } @@ -1456,7 +1569,7 @@ public static function mixin($mixin) * * @return bool */ - public static function hasMacro($name) + public static function hasMacro(string $name): bool { return isset(static::$macros[$name]); } @@ -1469,7 +1582,7 @@ public static function hasMacro($name) * * @return mixed */ - protected function callMacro($name, $parameters) + protected function callMacro(string $name, array $parameters) { $macro = static::$macros[$name]; @@ -1493,9 +1606,9 @@ protected function callMacro($name, $parameters) * * @throws BadFluentSetterException|Throwable * - * @return static + * @return static|int|float|string */ - public function __call($method, $parameters) + public function __call(string $method, array $parameters) { if (static::hasMacro($method)) { return static::bindMacroContext($this, function () use (&$method, &$parameters) { @@ -1528,7 +1641,7 @@ public function __call($method, $parameters) return $this; } - protected function getForHumansInitialVariables($syntax, $short) + protected function getForHumansInitialVariables($syntax, $short): array { if (\is_array($syntax)) { return $syntax; @@ -1559,31 +1672,34 @@ protected function getForHumansInitialVariables($syntax, $short) * * @return array */ - protected function getForHumansParameters($syntax = null, $short = false, $parts = -1, $options = null) + protected function getForHumansParameters($syntax = null, $short = false, $parts = self::NO_LIMIT, $options = null): array { $optionalSpace = ' '; $default = $this->getTranslationMessage('list.0') ?? $this->getTranslationMessage('list') ?? ' '; + /** @var bool|string $join */ $join = $default === '' ? '' : ' '; + /** @var bool|array|string $altNumbers */ $altNumbers = false; $aUnit = false; $minimumUnit = 's'; $skip = []; extract($this->getForHumansInitialVariables($syntax, $short)); - $skip = array_map('strtolower', array_filter((array) $skip, static function ($value) { - return \is_string($value) && $value !== ''; - })); + $skip = array_map( + static fn ($unit) => $unit instanceof Unit ? $unit->value : $unit, + (array) $skip, + ); + $skip = array_map( + 'strtolower', + array_filter($skip, static fn ($unit) => \is_string($unit) && $unit !== ''), + ); - if ($syntax === null) { - $syntax = CarbonInterface::DIFF_ABSOLUTE; - } + $syntax ??= CarbonInterface::DIFF_ABSOLUTE; - if ($parts === -1) { + if ($parts === self::NO_LIMIT) { $parts = INF; } - if ($options === null) { - $options = static::getHumanDiffOptions(); - } + $options ??= static::getHumanDiffOptions(); if ($join === false) { $join = ' '; @@ -1623,16 +1739,16 @@ protected function getForHumansParameters($syntax = null, $short = false, $parts } $glue = $join; - $join = function ($list) use ($glue) { - return implode($glue, $list); - }; + $join = static fn ($list) => implode($glue, $list); } $interpolations = [ ':optional-space' => $optionalSpace, ]; - return [$syntax, $short, $parts, $options, $join, $aUnit, $altNumbers, $interpolations, $minimumUnit, $skip]; + $translator ??= isset($locale) ? Translator::get($locale) : null; + + return [$syntax, $short, $parts, $options, $join, $aUnit, $altNumbers, $interpolations, $minimumUnit, $skip, $translator]; } protected static function getRoundingMethodFromOptions(int $options): ?string @@ -1657,7 +1773,7 @@ protected static function getRoundingMethodFromOptions(int $options): ?string * * @return int[] */ - public function toArray() + public function toArray(): array { return [ 'years' => $this->years, @@ -1676,7 +1792,7 @@ public function toArray() * * @return int[] */ - public function getNonZeroValues() + public function getNonZeroValues(): array { return array_filter($this->toArray(), 'intval'); } @@ -1687,7 +1803,7 @@ public function getNonZeroValues() * * @return int[] */ - public function getValuesSequence() + public function getValuesSequence(): array { $nonZeroValues = $this->getNonZeroValues(); @@ -1731,25 +1847,30 @@ public function getValuesSequence() * echo CarbonInterval::fromString('1d 24h')->forHumans(['minimumUnit' => 'hour']) . "\n"; * ``` * - * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains: - * - 'syntax' entry (see below) - * - 'short' entry (see below) - * - 'parts' entry (see below) - * - 'options' entry (see below) - * - 'skip' entry, list of units to skip (array of strings or a single string, + * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contain: + * ⦿ 'syntax' entry (see below) + * ⦿ 'short' entry (see below) + * ⦿ 'parts' entry (see below) + * ⦿ 'options' entry (see below) + * ⦿ 'skip' entry, list of units to skip (array of strings or a single string, * ` it can be the unit name (singular or plural) or its shortcut * ` (y, m, w, d, h, min, s, ms, µs). - * - 'aUnit' entry, prefer "an hour" over "1 hour" if true - * - 'join' entry determines how to join multiple parts of the string + * ⦿ 'aUnit' entry, prefer "an hour" over "1 hour" if true + * ⦿ 'altNumbers' entry, use alternative numbers if available + * ` (from the current language if true is passed, from the given language(s) + * ` if array or string is passed) + * ⦿ 'join' entry determines how to join multiple parts of the string * ` - if $join is a string, it's used as a joiner glue * ` - if $join is a callable/closure, it get the list of string and should return a string * ` - if $join is an array, the first item will be the default glue, and the second item * ` will be used instead of the glue for the last item * ` - if $join is true, it will be guessed from the locale ('list' translation file entry) * ` - if $join is missing, a space will be used as glue - * - 'minimumUnit' entry determines the smallest unit of time to display can be long or + * ⦿ 'minimumUnit' entry determines the smallest unit of time to display can be long or * ` short form of the units, e.g. 'hour' or 'h' (default value: s) - * if int passed, it add modifiers: + * ⦿ 'locale' language in which the diff should be output (has no effect if 'translator' key is set) + * ⦿ 'translator' a custom translator to use to translator the output. + * if int passed, it adds modifiers: * Possible values: * - CarbonInterface::DIFF_ABSOLUTE no modifiers * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier @@ -1763,9 +1884,10 @@ public function getValuesSequence() * * @return string */ - public function forHumans($syntax = null, $short = false, $parts = -1, $options = null) + public function forHumans($syntax = null, $short = false, $parts = self::NO_LIMIT, $options = null): string { - [$syntax, $short, $parts, $options, $join, $aUnit, $altNumbers, $interpolations, $minimumUnit, $skip] = $this + /* @var TranslatorInterface|null $translator */ + [$syntax, $short, $parts, $options, $join, $aUnit, $altNumbers, $interpolations, $minimumUnit, $skip, $translator] = $this ->getForHumansParameters($syntax, $short, $parts, $options); $interval = []; @@ -1779,8 +1901,7 @@ public function forHumans($syntax = null, $short = false, $parts = -1, $options $transId = $relativeToNow ? ($isFuture ? 'from_now' : 'ago') : ($isFuture ? 'after' : 'before'); $declensionMode = null; - /** @var \Symfony\Component\Translation\Translator $translator */ - $translator = $this->getLocalTranslator(); + $translator ??= $this->getLocalTranslator(); $handleDeclensions = function ($unit, $count, $index = 0, $parts = 1) use ($interpolations, $transId, $translator, $altNumbers, $absolute, &$declensionMode) { if (!$absolute) { @@ -1825,7 +1946,7 @@ public function forHumans($syntax = null, $short = false, $parts = -1, $options $intervalValues = $this->copy()->roundUnit( $keys[$index], 1, - $method + $method, ); $previousCount = $count; } @@ -1942,17 +2063,10 @@ public function forHumans($syntax = null, $short = false, $parts = -1, $options if ($parts === 1) { if ($relativeToNow && $unit === 'day') { - if ($count === 1 && $options & CarbonInterface::ONE_DAY_WORDS) { - $key = $isFuture ? 'diff_tomorrow' : 'diff_yesterday'; - $translation = $this->translate($key, $interpolations, null, $translator); - - if ($translation !== $key) { - return $translation; - } - } + $specialTranslations = static::SPECIAL_TRANSLATIONS[$count] ?? null; - if ($count === 2 && $options & CarbonInterface::TWO_DAY_WORDS) { - $key = $isFuture ? 'diff_after_tomorrow' : 'diff_before_yesterday'; + if ($specialTranslations && $options & $specialTranslations['option']) { + $key = $specialTranslations[$isFuture ? 'future' : 'past']; $translation = $this->translate($key, $interpolations, null, $translator); if ($translation !== $key) { @@ -1971,6 +2085,19 @@ public function forHumans($syntax = null, $short = false, $parts = -1, $options return $this->translate($transId, array_merge($time, $interpolations, $time), null, $translator); } + public function format(string $format): string + { + $output = parent::format($format); + + if (!str_contains($format, '%a') || !isset($this->startDate, $this->endDate)) { + return $output; + } + + $this->rawInterval ??= $this->startDate->diffAsDateInterval($this->endDate); + + return str_replace('(unknown)', $this->rawInterval->format('%a'), $output); + } + /** * Format the instance as a string using the forHumans() function. * @@ -1978,9 +2105,11 @@ public function forHumans($syntax = null, $short = false, $parts = -1, $options * * @return string */ - public function __toString() + public function __toString(): string { - $format = $this->localToStringFormat ?? static::$toStringFormat; + $format = $this->localToStringFormat + ?? $this->getFactory()->getSettings()['toStringFormat'] + ?? null; if (!$format) { return $this->forHumans(); @@ -2003,7 +2132,7 @@ public function __toString() * * @return DateInterval */ - public function toDateInterval() + public function toDateInterval(): DateInterval { return self::castIntervalToClass($this, DateInterval::class); } @@ -2015,17 +2144,50 @@ public function toDateInterval() * * @return CarbonPeriod */ - public function toPeriod(...$params) + public function toPeriod(...$params): CarbonPeriod + { + if ($this->timezoneSetting) { + $timeZone = \is_string($this->timezoneSetting) + ? new DateTimeZone($this->timezoneSetting) + : $this->timezoneSetting; + + if ($timeZone instanceof DateTimeZone) { + array_unshift($params, $timeZone); + } + } + + $class = ($params[0] ?? null) instanceof DateTime ? CarbonPeriod::class : CarbonPeriodImmutable::class; + + return $class::create($this, ...$params); + } + + /** + * Decompose the current interval into + * + * @param mixed|int|DateInterval|string|Closure|Unit|null $interval interval or number of the given $unit + * @param Unit|string|null $unit if specified, $interval must be an integer + * + * @return CarbonPeriod + */ + public function stepBy($interval, Unit|string|null $unit = null): CarbonPeriod { - if ($this->tzName) { - $tz = \is_string($this->tzName) ? new DateTimeZone($this->tzName) : $this->tzName; + $this->checkStartAndEnd(); + $start = $this->startDate ?? CarbonImmutable::make('now'); + $end = $this->endDate ?? $start->copy()->add($this); - if ($tz instanceof DateTimeZone) { - array_unshift($params, $tz); + try { + $step = static::make($interval, $unit); + } catch (InvalidFormatException $exception) { + if ($unit || (\is_string($interval) ? preg_match('/(\s|\d)/', $interval) : !($interval instanceof Unit))) { + throw $exception; } + + $step = static::make(1, $interval); } - return CarbonPeriod::create($this, ...$params); + $class = $start instanceof DateTime ? CarbonPeriod::class : CarbonPeriodImmutable::class; + + return $class::create($step, $start, $end); } /** @@ -2036,23 +2198,23 @@ public function toPeriod(...$params) * * @return $this */ - public function invert($inverted = null) + public function invert($inverted = null): static { $this->invert = (\func_num_args() === 0 ? !$this->invert : $inverted) ? 1 : 0; return $this; } - protected function solveNegativeInterval() + protected function solveNegativeInterval(): static { if (!$this->isEmpty() && $this->years <= 0 && $this->months <= 0 && $this->dayz <= 0 && $this->hours <= 0 && $this->minutes <= 0 && $this->seconds <= 0 && $this->microseconds <= 0) { - $this->years *= -1; - $this->months *= -1; - $this->dayz *= -1; - $this->hours *= -1; - $this->minutes *= -1; - $this->seconds *= -1; - $this->microseconds *= -1; + $this->years *= self::NEGATIVE; + $this->months *= self::NEGATIVE; + $this->dayz *= self::NEGATIVE; + $this->hours *= self::NEGATIVE; + $this->minutes *= self::NEGATIVE; + $this->seconds *= self::NEGATIVE; + $this->microseconds *= self::NEGATIVE; $this->invert(); } @@ -2067,13 +2229,13 @@ protected function solveNegativeInterval() * * @return $this */ - public function add($unit, $value = 1) + public function add($unit, $value = 1): static { if (is_numeric($unit)) { [$value, $unit] = [$unit, $value]; } - if (\is_string($unit) && !preg_match('/^\s*\d/', $unit)) { + if (\is_string($unit) && !preg_match('/^\s*-?\d/', $unit)) { $unit = "$value $unit"; $value = 1; } @@ -2088,7 +2250,7 @@ public function add($unit, $value = 1) $interval->times($value); } - $sign = ($this->invert === 1) !== ($interval->invert === 1) ? -1 : 1; + $sign = ($this->invert === 1) !== ($interval->invert === 1) ? self::NEGATIVE : self::POSITIVE; $this->years += $interval->y * $sign; $this->months += $interval->m * $sign; $this->dayz += ($interval->days === false ? $interval->d : $interval->days) * $sign; @@ -2110,7 +2272,7 @@ public function add($unit, $value = 1) * * @return $this */ - public function sub($unit, $value = 1) + public function sub($unit, $value = 1): static { if (is_numeric($unit)) { [$value, $unit] = [$unit, $value]; @@ -2127,7 +2289,7 @@ public function sub($unit, $value = 1) * * @return $this */ - public function subtract($unit, $value = 1) + public function subtract($unit, $value = 1): static { return $this->sub($unit, $value); } @@ -2155,7 +2317,7 @@ public function plus( $minutes = 0, $seconds = 0, $microseconds = 0 - ): self { + ): static { return $this->add(" $years years $months months $weeks weeks $days days $hours hours $minutes minutes $seconds seconds $microseconds microseconds @@ -2185,7 +2347,7 @@ public function minus( $minutes = 0, $seconds = 0, $microseconds = 0 - ): self { + ): static { return $this->sub(" $years years $months months $weeks weeks $days days $hours hours $minutes minutes $seconds seconds $microseconds microseconds @@ -2207,7 +2369,7 @@ public function minus( * * @return $this */ - public function times($factor) + public function times($factor): static { if ($factor < 0) { $this->invert = $this->invert ? 0 : 1; @@ -2240,12 +2402,12 @@ public function times($factor) * * @return $this */ - public function shares($divider) + public function shares($divider): static { return $this->times(1 / $divider); } - protected function copyProperties(self $interval, $ignoreSign = false) + protected function copyProperties(self $interval, $ignoreSign = false): static { $this->years = $interval->years; $this->months = $interval->months; @@ -2269,7 +2431,7 @@ protected function copyProperties(self $interval, $ignoreSign = false) * * @return $this */ - public function multiply($factor) + public function multiply($factor): static { if ($factor < 0) { $this->invert = $this->invert ? 0 : 1; @@ -2286,7 +2448,7 @@ public function multiply($factor) static::create($yearPart) ->microseconds(abs($this->totalMicroseconds) * $factor) ->cascade(), - true + true, ); } @@ -2297,7 +2459,7 @@ public function multiply($factor) * * @return $this */ - public function divide($divider) + public function divide($divider): static { return $this->multiply(1 / $divider); } @@ -2309,7 +2471,7 @@ public function divide($divider) * * @return string */ - public static function getDateIntervalSpec(DateInterval $interval, bool $microseconds = false, array $skip = []) + public static function getDateIntervalSpec(DateInterval $interval, bool $microseconds = false, array $skip = []): string { $date = array_filter([ static::PERIOD_YEARS => abs($interval->y), @@ -2317,6 +2479,8 @@ public static function getDateIntervalSpec(DateInterval $interval, bool $microse static::PERIOD_DAYS => abs($interval->d), ]); + $skip = array_map([Unit::class, 'toNameIfUnit'], $skip); + if ( $interval->days >= CarbonInterface::DAYS_PER_WEEK * CarbonInterface::WEEKS_PER_MONTH && (!isset($date[static::PERIOD_YEARS]) || \count(array_intersect(['y', 'year', 'years'], $skip))) && @@ -2329,7 +2493,7 @@ public static function getDateIntervalSpec(DateInterval $interval, bool $microse $seconds = abs($interval->s); if ($microseconds && $interval->f > 0) { - $seconds = sprintf('%d.%06d', $seconds, abs($interval->f) * 1000000); + $seconds = \sprintf('%d.%06d', $seconds, abs($interval->f) * 1000000); } $time = array_filter([ @@ -2359,7 +2523,7 @@ public static function getDateIntervalSpec(DateInterval $interval, bool $microse * * @return string */ - public function spec(bool $microseconds = false) + public function spec(bool $microseconds = false): string { return static::getDateIntervalSpec($this, $microseconds); } @@ -2370,22 +2534,15 @@ public function spec(bool $microseconds = false) * @param DateInterval $first * @param DateInterval $second * - * @return int + * @return int 0, 1 or -1 */ - public static function compareDateIntervals(DateInterval $first, DateInterval $second) + public static function compareDateIntervals(DateInterval $first, DateInterval $second): int { $current = Carbon::now(); $passed = $current->avoidMutation()->add($second); $current->add($first); - if ($current < $passed) { - return -1; - } - if ($current > $passed) { - return 1; - } - - return 0; + return $current <=> $passed; } /** @@ -2393,98 +2550,24 @@ public static function compareDateIntervals(DateInterval $first, DateInterval $s * * @param DateInterval $interval * - * @return int + * @return int 0, 1 or -1 */ - public function compare(DateInterval $interval) + public function compare(DateInterval $interval): int { return static::compareDateIntervals($this, $interval); } - private function invertCascade(array $values) + /** + * Convert overflowed values into bigger units. + * + * @return $this + */ + public function cascade(): static { - return $this->set(array_map(function ($value) { - return -$value; - }, $values))->doCascade(true)->invert(); + return $this->doCascade(false); } - private function doCascade(bool $deep) - { - $originalData = $this->toArray(); - $originalData['milliseconds'] = (int) ($originalData['microseconds'] / static::getMicrosecondsPerMillisecond()); - $originalData['microseconds'] = $originalData['microseconds'] % static::getMicrosecondsPerMillisecond(); - $originalData['weeks'] = (int) ($this->d / static::getDaysPerWeek()); - $originalData['daysExcludeWeeks'] = fmod($this->d, static::getDaysPerWeek()); - unset($originalData['days']); - $newData = $originalData; - $previous = []; - - foreach (self::getFlipCascadeFactors() as $source => [$target, $factor]) { - foreach (['source', 'target'] as $key) { - if ($$key === 'dayz') { - $$key = 'daysExcludeWeeks'; - } - } - - $value = $newData[$source]; - $modulo = fmod($factor + fmod($value, $factor), $factor); - $newData[$source] = $modulo; - $newData[$target] += ($value - $modulo) / $factor; - - $decimalPart = fmod($newData[$source], 1); - - if ($decimalPart !== 0.0) { - $unit = $source; - - foreach ($previous as [$subUnit, $subFactor]) { - $newData[$unit] -= $decimalPart; - $newData[$subUnit] += $decimalPart * $subFactor; - $decimalPart = fmod($newData[$subUnit], 1); - - if ($decimalPart === 0.0) { - break; - } - - $unit = $subUnit; - } - } - - array_unshift($previous, [$source, $factor]); - } - - $positive = null; - - if (!$deep) { - foreach ($newData as $value) { - if ($value) { - if ($positive === null) { - $positive = ($value > 0); - - continue; - } - - if (($value > 0) !== $positive) { - return $this->invertCascade($originalData) - ->solveNegativeInterval(); - } - } - } - } - - return $this->set($newData) - ->solveNegativeInterval(); - } - - /** - * Convert overflowed values into bigger units. - * - * @return $this - */ - public function cascade() - { - return $this->doCascade(false); - } - - public function hasNegativeValues(): bool + public function hasNegativeValues(): bool { foreach ($this->toArray() as $value) { if ($value < 0) { @@ -2515,7 +2598,7 @@ public function hasPositiveValues(): bool * * @return float */ - public function total($unit) + public function total(string $unit): float { $realUnit = $unit = strtolower($unit); @@ -2525,6 +2608,14 @@ public function total($unit) throw new UnknownUnitException($unit); } + $this->checkStartAndEnd(); + + if ($this->startDate && $this->endDate) { + $diff = $this->startDate->diffInUnit($unit, $this->endDate); + + return $this->absolute ? abs($diff) : $diff; + } + $result = 0; $cumulativeFactor = 0; $unitFound = false; @@ -2592,7 +2683,7 @@ public function total($unit) } if ($this->invert) { - $result *= -1; + $result *= self::NEGATIVE; } if ($unit === 'weeks') { @@ -2628,7 +2719,25 @@ public function equalTo($interval): bool { $interval = $this->resolveInterval($interval); - return $interval !== null && $this->totalMicroseconds === $interval->totalMicroseconds; + if ($interval === null) { + return false; + } + + $step = $this->getStep(); + + if ($step) { + return $step === $interval->getStep(); + } + + if ($this->isEmpty()) { + return $interval->isEmpty(); + } + + $cascadedInterval = $this->copy()->cascade(); + $comparedInterval = $interval->copy()->cascade(); + + return $cascadedInterval->invert === $comparedInterval->invert && + $cascadedInterval->getNonZeroValues() === $comparedInterval->getNonZeroValues(); } /** @@ -2786,7 +2895,7 @@ public function lessThanOrEqualTo($interval): bool * * @return bool */ - public function between($interval1, $interval2, $equal = true): bool + public function between($interval1, $interval2, bool $equal = true): bool { return $equal ? $this->greaterThanOrEqualTo($interval1) && $this->lessThanOrEqualTo($interval2) @@ -2850,7 +2959,7 @@ public function betweenExcluded($interval1, $interval2): bool * * @return bool */ - public function isBetween($interval1, $interval2, $equal = true): bool + public function isBetween($interval1, $interval2, bool $equal = true): bool { return $this->between($interval1, $interval2, $equal); } @@ -2858,15 +2967,9 @@ public function isBetween($interval1, $interval2, $equal = true): bool /** * Round the current instance at the given unit with given precision if specified and the given function. * - * @param string $unit - * @param float|int|string|DateInterval|null $precision - * @param string $function - * * @throws Exception - * - * @return $this */ - public function roundUnit($unit, $precision = 1, $function = 'round') + public function roundUnit(string $unit, DateInterval|string|int|float $precision = 1, string $function = 'round'): static { if (static::getCascadeFactors() !== static::getDefaultCascadeFactors()) { $value = $function($this->total($unit) / $precision) * $precision; @@ -2889,7 +2992,7 @@ public function roundUnit($unit, $precision = 1, $function = 'round') $this->copyProperties( $next ->roundUnit($unit, $precision, $function) - ->diffAsCarbonInterval($base) + ->diff($base), ); return $this->invert($inverted); @@ -2905,7 +3008,7 @@ public function roundUnit($unit, $precision = 1, $function = 'round') * * @return $this */ - public function floorUnit($unit, $precision = 1) + public function floorUnit(string $unit, $precision = 1): static { return $this->roundUnit($unit, $precision, 'floor'); } @@ -2920,7 +3023,7 @@ public function floorUnit($unit, $precision = 1) * * @return $this */ - public function ceilUnit($unit, $precision = 1) + public function ceilUnit(string $unit, $precision = 1): static { return $this->roundUnit($unit, $precision, 'ceil'); } @@ -2935,7 +3038,7 @@ public function ceilUnit($unit, $precision = 1) * * @return $this */ - public function round($precision = 1, $function = 'round') + public function round($precision = 1, string $function = 'round'): static { return $this->roundWith($precision, $function); } @@ -2943,13 +3046,11 @@ public function round($precision = 1, $function = 'round') /** * Round the current instance second with given precision if specified. * - * @param float|int|string|DateInterval|null $precision - * * @throws Exception * * @return $this */ - public function floor($precision = 1) + public function floor(DateInterval|string|float|int $precision = 1): static { return $this->round($precision, 'floor'); } @@ -2957,28 +3058,333 @@ public function floor($precision = 1) /** * Ceil the current instance second with given precision if specified. * - * @param float|int|string|DateInterval|null $precision - * * @throws Exception * * @return $this */ - public function ceil($precision = 1) + public function ceil(DateInterval|string|float|int $precision = 1): static { return $this->round($precision, 'ceil'); } - private function needsDeclension(string $mode, int $index, int $parts): bool + public function __unserialize(array $data): void { - switch ($mode) { - case 'last': - return $index === $parts - 1; - default: - return true; + $properties = array_combine( + array_map( + static fn (mixed $key) => \is_string($key) + ? str_replace('tzName', 'timezoneSetting', $key) + : $key, + array_keys($data), + ), + $data, + ); + + if (method_exists(parent::class, '__unserialize')) { + // PHP >= 8.2 + parent::__unserialize($properties); + + return; + } + + // PHP <= 8.1 + // @codeCoverageIgnoreStart + $properties = array_combine( + array_map( + static fn (string $property) => preg_replace('/^\0.+\0/', '', $property), + array_keys($data), + ), + $data, + ); + $localStrictMode = $this->localStrictModeEnabled; + $this->localStrictModeEnabled = false; + $days = $properties['days'] ?? false; + $this->days = $days === false ? false : (int) $days; + $this->y = (int) ($properties['y'] ?? 0); + $this->m = (int) ($properties['m'] ?? 0); + $this->d = (int) ($properties['d'] ?? 0); + $this->h = (int) ($properties['h'] ?? 0); + $this->i = (int) ($properties['i'] ?? 0); + $this->s = (int) ($properties['s'] ?? 0); + $this->f = (float) ($properties['f'] ?? 0.0); + // @phpstan-ignore-next-line + $this->weekday = (int) ($properties['weekday'] ?? 0); + // @phpstan-ignore-next-line + $this->weekday_behavior = (int) ($properties['weekday_behavior'] ?? 0); + // @phpstan-ignore-next-line + $this->first_last_day_of = (int) ($properties['first_last_day_of'] ?? 0); + $this->invert = (int) ($properties['invert'] ?? 0); + // @phpstan-ignore-next-line + $this->special_type = (int) ($properties['special_type'] ?? 0); + // @phpstan-ignore-next-line + $this->special_amount = (int) ($properties['special_amount'] ?? 0); + // @phpstan-ignore-next-line + $this->have_weekday_relative = (int) ($properties['have_weekday_relative'] ?? 0); + // @phpstan-ignore-next-line + $this->have_special_relative = (int) ($properties['have_special_relative'] ?? 0); + parent::__construct(self::getDateIntervalSpec($this)); + + foreach ($properties as $property => $value) { + if ($property === 'localStrictModeEnabled') { + continue; + } + + $this->$property = $value; + } + + $this->localStrictModeEnabled = $properties['localStrictModeEnabled'] ?? $localStrictMode; + // @codeCoverageIgnoreEnd + } + + /** + * @template T + * + * @param T $interval + * @param mixed $original + * + * @return T + */ + private static function withOriginal(mixed $interval, mixed $original): mixed + { + if ($interval instanceof self) { + $interval->originalInput = $original; + } + + return $interval; + } + + private static function standardizeUnit(string $unit): string + { + $unit = rtrim($unit, 'sz').'s'; + + return $unit === 'days' ? 'dayz' : $unit; + } + + private static function getFlipCascadeFactors(): array + { + if (!self::$flipCascadeFactors) { + self::$flipCascadeFactors = []; + + foreach (self::getCascadeFactors() as $to => [$factor, $from]) { + self::$flipCascadeFactors[self::standardizeUnit($from)] = [self::standardizeUnit($to), $factor]; + } + } + + return self::$flipCascadeFactors; + } + + /** + * @template T of DateInterval + * + * @param DateInterval $interval + * + * @psalm-param class-string $className + * + * @return T + */ + private static function castIntervalToClass(DateInterval $interval, string $className, array $skip = []): object + { + $mainClass = DateInterval::class; + + if (!is_a($className, $mainClass, true)) { + throw new InvalidCastException("$className is not a sub-class of $mainClass."); + } + + $microseconds = $interval->f; + $instance = self::buildInstance($interval, $className, $skip); + + if ($instance instanceof self) { + $instance->originalInput = $interval; + } + + if ($microseconds) { + $instance->f = $microseconds; + } + + if ($interval instanceof self && is_a($className, self::class, true)) { + self::copyStep($interval, $instance); + } + + self::copyNegativeUnits($interval, $instance); + + return self::withOriginal($instance, $interval); + } + + /** + * @template T of DateInterval + * + * @param DateInterval $interval + * + * @psalm-param class-string $className + * + * @return T + */ + private static function buildInstance( + DateInterval $interval, + string $className, + array $skip = [], + ): object { + $serialization = self::buildSerializationString($interval, $className, $skip); + + return match ($serialization) { + null => new $className(static::getDateIntervalSpec($interval, false, $skip)), + default => unserialize($serialization), + }; + } + + /** + * As demonstrated by rlanvin (https://github.com/rlanvin) in + * https://github.com/briannesbitt/Carbon/issues/3018#issuecomment-2888538438 + * + * Modifying the output of serialize() to change the class name and unserializing + * the tweaked string allows creating new interval instances where the ->days + * property can be set. It's not possible neither with `new` nto with `__set_state`. + * + * It has a non-negligible performance cost, so we'll use this method only if + * $interval->days !== false. + */ + private static function buildSerializationString( + DateInterval $interval, + string $className, + array $skip = [], + ): ?string { + if ($interval->days === false || PHP_VERSION_ID < 8_02_00 || $skip !== []) { + return null; + } + + // De-enhance CarbonInterval objects to be serializable back to DateInterval + if ($interval instanceof self && !is_a($className, self::class, true)) { + $interval = clone $interval; + unset($interval->timezoneSetting); + unset($interval->originalInput); + unset($interval->startDate); + unset($interval->endDate); + unset($interval->rawInterval); + unset($interval->absolute); + unset($interval->initialValues); + unset($interval->clock); + unset($interval->step); + unset($interval->localMonthsOverflow); + unset($interval->localYearsOverflow); + unset($interval->localStrictModeEnabled); + unset($interval->localHumanDiffOptions); + unset($interval->localToStringFormat); + unset($interval->localSerializer); + unset($interval->localMacros); + unset($interval->localGenericMacros); + unset($interval->localFormatFunction); + unset($interval->localTranslator); } + + $serialization = serialize($interval); + $inputClass = $interval::class; + $expectedStart = 'O:'.\strlen($inputClass).':"'.$inputClass.'":'; + + if (!str_starts_with($serialization, $expectedStart)) { + return null; // @codeCoverageIgnore + } + + return 'O:'.\strlen($className).':"'.$className.'":'.substr($serialization, \strlen($expectedStart)); } - private function checkIntegerValue(string $name, $value) + private static function copyStep(self $from, self $to): void + { + $to->setStep($from->getStep()); + } + + private static function copyNegativeUnits(DateInterval $from, DateInterval $to): void + { + $to->invert = $from->invert; + + foreach (['y', 'm', 'd', 'h', 'i', 's'] as $unit) { + if ($from->$unit < 0) { + self::setIntervalUnit($to, $unit, $to->$unit * self::NEGATIVE); + } + } + } + + private function invertCascade(array $values): static + { + return $this->set(array_map(function ($value) { + return -$value; + }, $values))->doCascade(true)->invert(); + } + + private function doCascade(bool $deep): static + { + $originalData = $this->toArray(); + $originalData['milliseconds'] = (int) ($originalData['microseconds'] / static::getMicrosecondsPerMillisecond()); + $originalData['microseconds'] = $originalData['microseconds'] % static::getMicrosecondsPerMillisecond(); + $originalData['weeks'] = (int) ($this->d / static::getDaysPerWeek()); + $originalData['daysExcludeWeeks'] = fmod($this->d, static::getDaysPerWeek()); + unset($originalData['days']); + $newData = $originalData; + $previous = []; + + foreach (self::getFlipCascadeFactors() as $source => [$target, $factor]) { + foreach (['source', 'target'] as $key) { + if ($$key === 'dayz') { + $$key = 'daysExcludeWeeks'; + } + } + + $value = $newData[$source]; + $modulo = fmod($factor + fmod($value, $factor), $factor); + $newData[$source] = $modulo; + $newData[$target] += ($value - $modulo) / $factor; + + $decimalPart = fmod($newData[$source], 1); + + if ($decimalPart !== 0.0) { + $unit = $source; + + foreach ($previous as [$subUnit, $subFactor]) { + $newData[$unit] -= $decimalPart; + $newData[$subUnit] += $decimalPart * $subFactor; + $decimalPart = fmod($newData[$subUnit], 1); + + if ($decimalPart === 0.0) { + break; + } + + $unit = $subUnit; + } + } + + array_unshift($previous, [$source, $factor]); + } + + $positive = null; + + if (!$deep) { + foreach ($newData as $value) { + if ($value) { + if ($positive === null) { + $positive = ($value > 0); + + continue; + } + + if (($value > 0) !== $positive) { + return $this->invertCascade($originalData) + ->solveNegativeInterval(); + } + } + } + } + + return $this->set($newData) + ->solveNegativeInterval(); + } + + private function needsDeclension(string $mode, int $index, int $parts): bool + { + return match ($mode) { + 'last' => $index === $parts - 1, + default => true, + }; + } + + private function checkIntegerValue(string $name, mixed $value): void { if (\is_int($value)) { return; @@ -2998,7 +3404,7 @@ private function checkIntegerValue(string $name, $value) "From 3.0.0, decimal part will no longer be truncated and will be cascaded to smaller units.\n". "- To maintain the current behavior, use explicit cast: $name((int) \$value)\n". "- To adopt the new behavior globally, call CarbonInterval::enableFloatSetters()\n", - \E_USER_DEPRECATED + \E_USER_DEPRECATED, ); } } @@ -3006,14 +3412,14 @@ private function checkIntegerValue(string $name, $value) /** * Throw an exception if precision loss when storing the given value as an integer would be >= 1.0. */ - private function assertSafeForInteger(string $name, $value) + private function assertSafeForInteger(string $name, mixed $value): void { if ($value && !\is_int($value) && ($value >= 0x7fffffffffffffff || $value <= -0x7fffffffffffffff)) { throw new OutOfRangeException($name, -0x7fffffffffffffff, 0x7fffffffffffffff, $value); } } - private function handleDecimalPart(string $unit, $value, $integerValue) + private function handleDecimalPart(string $unit, mixed $value, mixed $integerValue): void { if (self::$floatSettersEnabled) { $floatValue = (float) $value; @@ -3051,4 +3457,116 @@ private function handleDecimalPart(string $unit, $value, $integerValue) $this->add($unit, $floatValue - $base); } } + + private function getInnerValues(): array + { + return [$this->y, $this->m, $this->d, $this->h, $this->i, $this->s, $this->f, $this->invert, $this->days]; + } + + private function checkStartAndEnd(): void + { + if ( + $this->initialValues !== null + && ($this->startDate !== null || $this->endDate !== null) + && $this->initialValues !== $this->getInnerValues() + ) { + $this->absolute = false; + $this->startDate = null; + $this->endDate = null; + $this->rawInterval = null; + } + } + + /** @return $this */ + private function setSetting(string $setting, mixed $value): self + { + switch ($setting) { + case 'timezoneSetting': + return $value === null ? $this : $this->setTimezone($value); + + case 'step': + $this->setStep($value); + + return $this; + + case 'localMonthsOverflow': + return $value === null ? $this : $this->settings(['monthOverflow' => $value]); + + case 'localYearsOverflow': + return $value === null ? $this : $this->settings(['yearOverflow' => $value]); + + case 'localStrictModeEnabled': + case 'localHumanDiffOptions': + case 'localToStringFormat': + case 'localSerializer': + case 'localMacros': + case 'localGenericMacros': + case 'localFormatFunction': + case 'localTranslator': + $this->$setting = $value; + + return $this; + + default: + // Drop unknown settings + return $this; + } + } + + private static function incrementUnit(DateInterval $instance, string $unit, int $value): void + { + if ($value === 0) { + return; + } + + // @codeCoverageIgnoreStart + if (PHP_VERSION_ID !== 8_03_20) { + $instance->$unit += $value; + + return; + } + + // Cannot use +=, nor set to a negative value directly as it segfaults in PHP 8.3.20 + self::setIntervalUnit($instance, $unit, ($instance->$unit ?? 0) + $value); + // @codeCoverageIgnoreEnd + } + + /** @codeCoverageIgnore */ + private static function setIntervalUnit(DateInterval $instance, string $unit, mixed $value): void + { + switch ($unit) { + case 'y': + $instance->y = $value; + + break; + + case 'm': + $instance->m = $value; + + break; + + case 'd': + $instance->d = $value; + + break; + + case 'h': + $instance->h = $value; + + break; + + case 'i': + $instance->i = $value; + + break; + + case 's': + $instance->s = $value; + + break; + + default: + $instance->$unit = $value; + } + } } diff --git a/vendor/nesbot/carbon/src/Carbon/CarbonPeriod.php b/vendor/nesbot/carbon/src/Carbon/CarbonPeriod.php index d12a98697..7ae64ebf8 100644 --- a/vendor/nesbot/carbon/src/Carbon/CarbonPeriod.php +++ b/vendor/nesbot/carbon/src/Carbon/CarbonPeriod.php @@ -1,5 +1,7 @@ rewind(); + + while ($this->valid()) { + $key = $this->key(); + $value = $this->current(); + + yield $key => $value; + + $this->next(); + } + } /** * Make a CarbonPeriod instance from given variable if possible. - * - * @param mixed $var - * - * @return static|null */ - public static function make($var) + public static function make(mixed $var): ?static { try { return static::instance($var); - } catch (NotAPeriodException $e) { + } catch (NotAPeriodException) { return static::create($var); } } /** * Create a new instance from a DatePeriod or CarbonPeriod object. - * - * @param CarbonPeriod|DatePeriod $period - * - * @return static */ - public static function instance($period) + public static function instance(mixed $period): static { if ($period instanceof static) { return $period->copy(); @@ -384,9 +363,9 @@ public static function instance($period) if ($period instanceof self) { return new static( $period->getStartDate(), - $period->getEndDate() ?: $period->getRecurrences(), + $period->getEndDate() ?? $period->getRecurrences(), $period->getDateInterval(), - $period->getOptions() + $period->getOptions(), ); } @@ -395,71 +374,61 @@ public static function instance($period) $period->start, $period->end ?: ($period->recurrences - 1), $period->interval, - $period->include_start_date ? 0 : static::EXCLUDE_START_DATE + $period->include_start_date ? 0 : static::EXCLUDE_START_DATE, ); } $class = static::class; $type = \gettype($period); + $chunks = explode('::', __METHOD__); throw new NotAPeriodException( - 'Argument 1 passed to '.$class.'::'.__METHOD__.'() '. + 'Argument 1 passed to '.$class.'::'.end($chunks).'() '. 'must be an instance of DatePeriod or '.$class.', '. - ($type === 'object' ? 'instance of '.\get_class($period) : $type).' given.' + ($type === 'object' ? 'instance of '.\get_class($period) : $type).' given.', ); } /** * Create a new instance. - * - * @return static */ - public static function create(...$params) + public static function create(...$params): static { return static::createFromArray($params); } /** * Create a new instance from an array of parameters. - * - * @param array $params - * - * @return static */ - public static function createFromArray(array $params) + public static function createFromArray(array $params): static { return new static(...$params); } /** * Create CarbonPeriod from ISO 8601 string. - * - * @param string $iso - * @param int|null $options - * - * @return static */ - public static function createFromIso($iso, $options = null) + public static function createFromIso(string $iso, ?int $options = null): static { $params = static::parseIso8601($iso); $instance = static::createFromArray($params); - if ($options !== null) { - $instance->setOptions($options); - } + $instance->options = ($instance instanceof CarbonPeriodImmutable ? static::IMMUTABLE : 0) | $options; + $instance->handleChangedParameters(); return $instance; } + public static function createFromISO8601String(string $iso, ?int $options = null): static + { + return self::createFromIso($iso, $options); + } + /** - * Return whether given interval contains non zero value of any time unit. - * - * @param \DateInterval $interval - * - * @return bool + * Return whether the given interval contains non-zero value of any time unit. */ - protected static function intervalHasTime(DateInterval $interval) + protected static function intervalHasTime(DateInterval $interval): bool { return $interval->h || $interval->i || $interval->s || $interval->f; } @@ -468,13 +437,9 @@ protected static function intervalHasTime(DateInterval $interval) * Return whether given variable is an ISO 8601 specification. * * Note: Check is very basic, as actual validation will be done later when parsing. - * We just want to ensure that variable is not any other type of a valid parameter. - * - * @param mixed $var - * - * @return bool + * We just want to ensure that variable is not any other type of valid parameter. */ - protected static function isIso8601($var) + protected static function isIso8601(mixed $var): bool { if (!\is_string($var)) { return false; @@ -491,13 +456,9 @@ protected static function isIso8601($var) /** * Parse given ISO 8601 string into an array of arguments. * - * @SuppressWarnings(PHPMD.ElseExpression) - * - * @param string $iso - * - * @return array + * @SuppressWarnings(ElseExpression) */ - protected static function parseIso8601($iso) + protected static function parseIso8601(string $iso): array { $result = []; @@ -509,7 +470,7 @@ protected static function parseIso8601($iso) foreach (explode('/', $iso) as $key => $part) { if ($key === 0 && preg_match('/^R(\d*|INF)$/', $part, $match)) { $parsed = \strlen($match[1]) ? (($match[1] !== 'INF') ? (int) $match[1] : INF) : null; - } elseif ($interval === null && $parsed = CarbonInterval::make($part)) { + } elseif ($interval === null && $parsed = self::makeInterval($part)) { $interval = $part; } elseif ($start === null && $parsed = $dateClass::make($part)) { $start = $part; @@ -527,13 +488,8 @@ protected static function parseIso8601($iso) /** * Add missing parts of the target date from the source date. - * - * @param string $source - * @param string $target - * - * @return string */ - protected static function addMissingParts($source, $target) + protected static function addMissingParts(string $source, string $target): string { $pattern = '/'.preg_replace('/\d+/', '[0-9]+', preg_quote($target, '/')).'$/'; @@ -542,9 +498,33 @@ protected static function addMissingParts($source, $target) return $count ? $result : $target; } + private static function makeInterval(mixed $input): ?CarbonInterval + { + try { + return CarbonInterval::make($input); + } catch (Throwable) { + return null; + } + } + + private static function makeTimezone(mixed $input): ?CarbonTimeZone + { + if (!\is_string($input)) { + return null; + } + + try { + return CarbonTimeZone::create($input); + } catch (Throwable) { + return null; + } + } + /** * Register a custom macro. * + * Pass null macro to remove it. + * * @example * ``` * CarbonPeriod::macro('middle', function () { @@ -553,12 +533,9 @@ protected static function addMissingParts($source, $target) * echo CarbonPeriod::since('2011-05-12')->until('2011-06-03')->middle(); * ``` * - * @param string $name - * @param object|callable $macro - * - * @return void + * @param-closure-this static $macro */ - public static function macro($name, $macro) + public static function macro(string $name, ?callable $macro): void { static::$macros[$name] = $macro; } @@ -591,45 +568,30 @@ public static function macro($name, $macro) * echo CarbonPeriod::create('2000-01-01', '2000-02-01')->addDays(5)->subDays(3); * ``` * - * @param object|string $mixin - * * @throws ReflectionException - * - * @return void */ - public static function mixin($mixin) + public static function mixin(object|string $mixin): void { static::baseMixin($mixin); } /** * Check if macro is registered. - * - * @param string $name - * - * @return bool */ - public static function hasMacro($name) + public static function hasMacro(string $name): bool { return isset(static::$macros[$name]); } /** * Provide static proxy for instance aliases. - * - * @param string $method - * @param array $parameters - * - * @return mixed */ - public static function __callStatic($method, $parameters) + public static function __callStatic(string $method, array $parameters): mixed { $date = new static(); if (static::hasMacro($method)) { - return static::bindMacroContext(null, function () use (&$method, &$parameters, &$date) { - return $date->callMacro($method, $parameters); - }); + return static::bindMacroContext(null, static fn () => $date->callMacro($method, $parameters)); } return $date->$method(...$parameters); @@ -638,14 +600,23 @@ public static function __callStatic($method, $parameters) /** * CarbonPeriod constructor. * - * @SuppressWarnings(PHPMD.ElseExpression) + * @SuppressWarnings(ElseExpression) * * @throws InvalidArgumentException */ public function __construct(...$arguments) { - if (is_a($this->dateClass, DateTimeImmutable::class, true)) { - $this->options = static::IMMUTABLE; + $raw = null; + + if (isset($arguments['raw'])) { + $raw = $arguments['raw']; + $this->isDefaultInterval = $arguments['isDefaultInterval'] ?? false; + + if (isset($arguments['dateClass'])) { + $this->dateClass = $arguments['dateClass']; + } + + $arguments = $raw; } // Parse and assign arguments one by one. First argument may be an ISO 8601 spec, @@ -658,56 +629,87 @@ public function __construct(...$arguments) } if ($argumentsCount === 1) { - if ($arguments[0] instanceof DatePeriod) { + if ($arguments[0] instanceof self) { + $arguments = [ + $arguments[0]->getStartDate(), + $arguments[0]->getEndDate() ?? $arguments[0]->getRecurrences(), + $arguments[0]->getDateInterval(), + $arguments[0]->getOptions(), + ]; + } elseif ($arguments[0] instanceof DatePeriod) { $arguments = [ $arguments[0]->start, $arguments[0]->end ?: ($arguments[0]->recurrences - 1), $arguments[0]->interval, $arguments[0]->include_start_date ? 0 : static::EXCLUDE_START_DATE, ]; - } elseif ($arguments[0] instanceof self) { - $arguments = [ - $arguments[0]->getStartDate(), - $arguments[0]->getEndDate() ?: $arguments[0]->getRecurrences(), - $arguments[0]->getDateInterval(), - $arguments[0]->getOptions(), - ]; } } + if (is_a($this->dateClass, DateTimeImmutable::class, true)) { + $this->options = static::IMMUTABLE; + } + $optionsSet = false; + $originalArguments = []; + $sortedArguments = []; foreach ($arguments as $argument) { $parsedDate = null; if ($argument instanceof DateTimeZone) { - $this->setTimezone($argument); - } elseif ($this->dateInterval === null && + $sortedArguments = $this->configureTimezone($argument, $sortedArguments, $originalArguments); + } elseif (!isset($sortedArguments['interval']) && ( (\is_string($argument) && preg_match( '/^(-?\d(\d(?![\/-])|[^\d\/-]([\/-])?)*|P[T\d].*|(?:\h*\d+(?:\.\d+)?\h*[a-z]+)+)$/i', - $argument + $argument, )) || $argument instanceof DateInterval || - $argument instanceof Closure + $argument instanceof Closure || + $argument instanceof Unit ) && - $parsedInterval = @CarbonInterval::make($argument) + $parsedInterval = self::makeInterval($argument) + ) { + $sortedArguments['interval'] = $parsedInterval; + } elseif (!isset($sortedArguments['start']) && $parsedDate = $this->makeDateTime($argument)) { + $sortedArguments['start'] = $parsedDate; + $originalArguments['start'] = $argument; + } elseif (!isset($sortedArguments['end']) && ($parsedDate = $parsedDate ?? $this->makeDateTime($argument))) { + $sortedArguments['end'] = $parsedDate; + $originalArguments['end'] = $argument; + } elseif (!isset($sortedArguments['recurrences']) && + !isset($sortedArguments['end']) && + (\is_int($argument) || \is_float($argument)) + && $argument >= 0 ) { - $this->setDateInterval($parsedInterval); - } elseif ($this->startDate === null && $parsedDate = $this->makeDateTime($argument)) { - $this->setStartDate($parsedDate); - } elseif ($this->endDate === null && ($parsedDate = $parsedDate ?? $this->makeDateTime($argument))) { - $this->setEndDate($parsedDate); - } elseif ($this->recurrences === null && $this->endDate === null && is_numeric($argument)) { - $this->setRecurrences($argument); + $sortedArguments['recurrences'] = $argument; } elseif (!$optionsSet && (\is_int($argument) || $argument === null)) { $optionsSet = true; - $this->setOptions(((int) $this->options) | ((int) $argument)); + $sortedArguments['options'] = (((int) $this->options) | ((int) $argument)); + } elseif ($parsedTimezone = self::makeTimezone($argument)) { + $sortedArguments = $this->configureTimezone($parsedTimezone, $sortedArguments, $originalArguments); } else { throw new InvalidPeriodParameterException('Invalid constructor parameters.'); } } + if ($raw === null && isset($sortedArguments['start'])) { + $end = $sortedArguments['end'] ?? max(1, $sortedArguments['recurrences'] ?? 1); + + if (\is_float($end)) { + $end = $end === INF ? PHP_INT_MAX : (int) round($end); + } + + $raw = [ + $sortedArguments['start'], + $sortedArguments['interval'] ?? CarbonInterval::day(), + $end, + ]; + } + + $this->setFromAssociativeArray($sortedArguments); + if ($this->startDate === null) { $dateClass = $this->dateClass; $this->setStartDate($dateClass::now()); @@ -723,15 +725,20 @@ public function __construct(...$arguments) $this->setOptions(0); } + parent::__construct( + $this->startDate, + $this->dateInterval, + $this->endDate ?? max(1, min(2147483639, $this->recurrences ?? 1)), + $this->options, + ); + $this->constructed = true; } /** * Get a copy of the instance. - * - * @return static */ - public function copy() + public function copy(): static { return clone $this; } @@ -739,44 +746,34 @@ public function copy() /** * Prepare the instance to be set (self if mutable to be mutated, * copy if immutable to generate a new instance). - * - * @return static */ - protected function copyIfImmutable() + protected function copyIfImmutable(): static { return $this; } /** * Get the getter for a property allowing both `DatePeriod` snakeCase and camelCase names. - * - * @param string $name - * - * @return callable|null */ - protected function getGetter(string $name) + protected function getGetter(string $name): ?callable { - switch (strtolower(preg_replace('/[A-Z]/', '_$0', $name))) { - case 'start': - case 'start_date': - return [$this, 'getStartDate']; - case 'end': - case 'end_date': - return [$this, 'getEndDate']; - case 'interval': - case 'date_interval': - return [$this, 'getDateInterval']; - case 'recurrences': - return [$this, 'getRecurrences']; - case 'include_start_date': - return [$this, 'isStartIncluded']; - case 'include_end_date': - return [$this, 'isEndIncluded']; - case 'current': - return [$this, 'current']; - default: - return null; - } + return match (strtolower(preg_replace('/[A-Z]/', '_$0', $name))) { + 'start', 'start_date' => [$this, 'getStartDate'], + 'end', 'end_date' => [$this, 'getEndDate'], + 'interval', 'date_interval' => [$this, 'getDateInterval'], + 'recurrences' => [$this, 'getRecurrences'], + 'include_start_date' => [$this, 'isStartIncluded'], + 'include_end_date' => [$this, 'isEndIncluded'], + 'current' => [$this, 'current'], + 'locale' => [$this, 'locale'], + 'tzname', 'tz_name' => fn () => match (true) { + $this->timezoneSetting === null => null, + \is_string($this->timezoneSetting) => $this->timezoneSetting, + $this->timezoneSetting instanceof DateTimeZone => $this->timezoneSetting->getName(), + default => CarbonTimeZone::instance($this->timezoneSetting)->getName(), + }, + default => null, + }; } /** @@ -871,15 +868,24 @@ public function getDateClass(): string /** * Change the period date interval. * - * @param DateInterval|string $interval + * @param DateInterval|Unit|string|int $interval + * @param Unit|string $unit the unit of $interval if it's a number * * @throws InvalidIntervalException * * @return static */ - public function setDateInterval($interval) + public function setDateInterval(mixed $interval, Unit|string|null $unit = null): static { - if (!$interval = CarbonInterval::make($interval)) { + if ($interval instanceof Unit) { + $interval = $interval->interval(); + } + + if ($unit instanceof Unit) { + $unit = $unit->name; + } + + if (!$interval = CarbonInterval::make($interval, $unit)) { throw new InvalidIntervalException('Invalid interval.'); } @@ -898,11 +904,25 @@ public function setDateInterval($interval) } /** - * Invert the period date interval. + * Reset the date interval to the default value. * - * @return static + * Difference with simply setting interval to 1-day is that P1D will not appear when calling toIso8601String() + * and also next adding to the interval won't include the default 1-day. + */ + public function resetDateInterval(): static + { + $self = $this->copyIfImmutable(); + $self->setDateInterval(CarbonInterval::day()); + + $self->isDefaultInterval = true; + + return $self; + } + + /** + * Invert the period date interval. */ - public function invertDateInterval() + public function invertDateInterval(): static { return $this->setDateInterval($this->dateInterval->invert()); } @@ -915,7 +935,7 @@ public function invertDateInterval() * * @return static */ - public function setDates($start, $end) + public function setDates(mixed $start, mixed $end): static { return $this->setStartDate($start)->setEndDate($end); } @@ -925,18 +945,12 @@ public function setDates($start, $end) * * @param int|null $options * - * @throws InvalidArgumentException - * * @return static */ - public function setOptions($options) + public function setOptions(?int $options): static { - if (!\is_int($options) && $options !== null) { - throw new InvalidPeriodParameterException('Invalid options.'); - } - $self = $this->copyIfImmutable(); - $self->options = $options ?: 0; + $self->options = $options ?? 0; $self->handleChangedParameters(); @@ -945,12 +959,10 @@ public function setOptions($options) /** * Get the period options. - * - * @return int */ - public function getOptions() + public function getOptions(): int { - return $this->options; + return $this->options ?? 0; } /** @@ -959,53 +971,45 @@ public function getOptions() * @param int $options * @param bool|null $state * - * @throws \InvalidArgumentException + * @throws InvalidArgumentException * * @return static */ - public function toggleOptions($options, $state = null) + public function toggleOptions(int $options, ?bool $state = null): static { + $self = $this->copyIfImmutable(); + if ($state === null) { $state = ($this->options & $options) !== $options; } - return $this->setOptions( + return $self->setOptions( $state ? $this->options | $options : - $this->options & ~$options + $this->options & ~$options, ); } /** * Toggle EXCLUDE_START_DATE option. - * - * @param bool $state - * - * @return static */ - public function excludeStartDate($state = true) + public function excludeStartDate(bool $state = true): static { return $this->toggleOptions(static::EXCLUDE_START_DATE, $state); } /** * Toggle EXCLUDE_END_DATE option. - * - * @param bool $state - * - * @return static */ - public function excludeEndDate($state = true) + public function excludeEndDate(bool $state = true): static { return $this->toggleOptions(static::EXCLUDE_END_DATE, $state); } /** * Get the underlying date interval. - * - * @return CarbonInterval */ - public function getDateInterval() + public function getDateInterval(): CarbonInterval { return $this->dateInterval->copy(); } @@ -1014,10 +1018,8 @@ public function getDateInterval() * Get start date of the period. * * @param string|null $rounding Optional rounding 'floor', 'ceil', 'round' using the period interval. - * - * @return CarbonInterface */ - public function getStartDate(string $rounding = null) + public function getStartDate(?string $rounding = null): CarbonInterface { $date = $this->startDate->avoidMutation(); @@ -1028,10 +1030,8 @@ public function getStartDate(string $rounding = null) * Get end date of the period. * * @param string|null $rounding Optional rounding 'floor', 'ceil', 'round' using the period interval. - * - * @return CarbonInterface|null */ - public function getEndDate(string $rounding = null) + public function getEndDate(?string $rounding = null): ?CarbonInterface { if (!$this->endDate) { return null; @@ -1044,60 +1044,49 @@ public function getEndDate(string $rounding = null) /** * Get number of recurrences. - * - * @return int|float|null */ - public function getRecurrences() + #[ReturnTypeWillChange] + public function getRecurrences(): int|float|null { - return $this->recurrences; + return $this->carbonRecurrences; } /** * Returns true if the start date should be excluded. - * - * @return bool */ - public function isStartExcluded() + public function isStartExcluded(): bool { return ($this->options & static::EXCLUDE_START_DATE) !== 0; } /** * Returns true if the end date should be excluded. - * - * @return bool */ - public function isEndExcluded() + public function isEndExcluded(): bool { return ($this->options & static::EXCLUDE_END_DATE) !== 0; } /** * Returns true if the start date should be included. - * - * @return bool */ - public function isStartIncluded() + public function isStartIncluded(): bool { return !$this->isStartExcluded(); } /** * Returns true if the end date should be included. - * - * @return bool */ - public function isEndIncluded() + public function isEndIncluded(): bool { return !$this->isEndExcluded(); } /** * Return the start if it's included by option, else return the start + 1 period interval. - * - * @return CarbonInterface */ - public function getIncludedStartDate() + public function getIncludedStartDate(): CarbonInterface { $start = $this->getStartDate(); @@ -1111,10 +1100,8 @@ public function getIncludedStartDate() /** * Return the end if it's included by option, else return the end - 1 period interval. * Warning: if the period has no fixed end, this method will iterate the period to calculate it. - * - * @return CarbonInterface */ - public function getIncludedEndDate() + public function getIncludedEndDate(): CarbonInterface { $end = $this->getEndDate(); @@ -1132,14 +1119,9 @@ public function getIncludedEndDate() /** * Add a filter to the stack. * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * - * @param callable $callback - * @param string $name - * - * @return static + * @SuppressWarnings(UnusedFormalParameter) */ - public function addFilter($callback, $name = null) + public function addFilter(callable|string $callback, ?string $name = null): static { $self = $this->copyIfImmutable(); $tuple = $self->createFilterTuple(\func_get_args()); @@ -1154,14 +1136,9 @@ public function addFilter($callback, $name = null) /** * Prepend a filter to the stack. * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * - * @param callable $callback - * @param string $name - * - * @return static + * @SuppressWarnings(UnusedFormalParameter) */ - public function prependFilter($callback, $name = null) + public function prependFilter(callable|string $callback, ?string $name = null): static { $self = $this->copyIfImmutable(); $tuple = $self->createFilterTuple(\func_get_args()); @@ -1175,21 +1152,15 @@ public function prependFilter($callback, $name = null) /** * Remove a filter by instance or name. - * - * @param callable|string $filter - * - * @return static */ - public function removeFilter($filter) + public function removeFilter(callable|string $filter): static { $self = $this->copyIfImmutable(); $key = \is_callable($filter) ? 0 : 1; $self->filters = array_values(array_filter( $this->filters, - function ($tuple) use ($key, $filter) { - return $tuple[$key] !== $filter; - } + static fn ($tuple) => $tuple[$key] !== $filter, )); $self->updateInternalState(); @@ -1201,12 +1172,8 @@ function ($tuple) use ($key, $filter) { /** * Return whether given instance or name is in the filter stack. - * - * @param callable|string $filter - * - * @return bool */ - public function hasFilter($filter) + public function hasFilter(callable|string $filter): bool { $key = \is_callable($filter) ? 0 : 1; @@ -1221,22 +1188,16 @@ public function hasFilter($filter) /** * Get filters stack. - * - * @return array */ - public function getFilters() + public function getFilters(): array { return $this->filters; } /** * Set filters stack. - * - * @param array $filters - * - * @return static */ - public function setFilters(array $filters) + public function setFilters(array $filters): static { $self = $this->copyIfImmutable(); $self->filters = $filters; @@ -1250,10 +1211,8 @@ public function setFilters(array $filters) /** * Reset filters stack. - * - * @return static */ - public function resetFilters() + public function resetFilters(): static { $self = $this->copyIfImmutable(); $self->filters = []; @@ -1262,7 +1221,7 @@ public function resetFilters() $self->filters[] = [static::END_DATE_FILTER, null]; } - if ($self->recurrences !== null) { + if ($self->carbonRecurrences !== null) { $self->filters[] = [static::RECURRENCES_FILTER, null]; } @@ -1274,25 +1233,21 @@ public function resetFilters() /** * Add a recurrences filter (set maximum number of recurrences). * - * @param int|float|null $recurrences - * * @throws InvalidArgumentException - * - * @return static */ - public function setRecurrences($recurrences) + public function setRecurrences(int|float|null $recurrences): static { - if ((!is_numeric($recurrences) && $recurrences !== null) || $recurrences < 0) { - throw new InvalidPeriodParameterException('Invalid number of recurrences.'); - } - if ($recurrences === null) { return $this->removeFilter(static::RECURRENCES_FILTER); } + if ($recurrences < 0) { + throw new InvalidPeriodParameterException('Invalid number of recurrences.'); + } + /** @var self $self */ $self = $this->copyIfImmutable(); - $self->recurrences = $recurrences === INF ? INF : (int) $recurrences; + $self->carbonRecurrences = $recurrences === INF ? INF : (int) $recurrences; if (!$self->hasFilter(static::RECURRENCES_FILTER)) { return $self->addFilter(static::RECURRENCES_FILTER); @@ -1313,9 +1268,9 @@ public function setRecurrences($recurrences) * * @return static */ - public function setStartDate($date, $inclusive = null) + public function setStartDate(mixed $date, ?bool $inclusive = null): static { - if (!$this->isInfiniteDate($date) && !($date = ([$this->dateClass, 'make'])($date))) { + if (!$this->isInfiniteDate($date) && !($date = ([$this->dateClass, 'make'])($date, $this->timezone))) { throw new InvalidPeriodDateException('Invalid start date.'); } @@ -1339,9 +1294,9 @@ public function setStartDate($date, $inclusive = null) * * @return static */ - public function setEndDate($date, $inclusive = null) + public function setEndDate(mixed $date, ?bool $inclusive = null): static { - if ($date !== null && !$this->isInfiniteDate($date) && !$date = ([$this->dateClass, 'make'])($date)) { + if ($date !== null && !$this->isInfiniteDate($date) && !$date = ([$this->dateClass, 'make'])($date, $this->timezone)) { throw new InvalidPeriodDateException('Invalid end date.'); } @@ -1367,22 +1322,16 @@ public function setEndDate($date, $inclusive = null) /** * Check if the current position is valid. - * - * @return bool */ - #[ReturnTypeWillChange] - public function valid() + public function valid(): bool { return $this->validateCurrentDate() === true; } /** * Return the current key. - * - * @return int|null */ - #[ReturnTypeWillChange] - public function key() + public function key(): ?int { return $this->valid() ? $this->key @@ -1391,14 +1340,11 @@ public function key() /** * Return the current date. - * - * @return CarbonInterface|null */ - #[ReturnTypeWillChange] - public function current() + public function current(): ?CarbonInterface { return $this->valid() - ? $this->prepareForReturn($this->current) + ? $this->prepareForReturn($this->carbonCurrent) : null; } @@ -1406,13 +1352,10 @@ public function current() * Move forward to the next date. * * @throws RuntimeException - * - * @return void */ - #[ReturnTypeWillChange] - public function next() + public function next(): void { - if ($this->current === null) { + if ($this->carbonCurrent === null) { $this->rewind(); } @@ -1433,25 +1376,22 @@ public function next() * @see https://wiki.php.net/rfc/datetime_and_daylight_saving_time * * @throws RuntimeException - * - * @return void */ - #[ReturnTypeWillChange] - public function rewind() + public function rewind(): void { $this->key = 0; - $this->current = ([$this->dateClass, 'make'])($this->startDate); + $this->carbonCurrent = ([$this->dateClass, 'make'])($this->startDate); $settings = $this->getSettings(); if ($this->hasLocalTranslator()) { $settings['locale'] = $this->getTranslatorLocale(); } - $this->current->settings($settings); - $this->timezone = static::intervalHasTime($this->dateInterval) ? $this->current->getTimezone() : null; + $this->carbonCurrent->settings($settings); + $this->timezone = static::intervalHasTime($this->dateInterval) ? $this->carbonCurrent->getTimezone() : null; if ($this->timezone) { - $this->current = $this->current->utc(); + $this->carbonCurrent = $this->carbonCurrent->utc(); } $this->validationResult = null; @@ -1468,7 +1408,7 @@ public function rewind() * * @return bool */ - public function skip($count = 1) + public function skip(int $count = 1): bool { for ($i = $count; $this->valid() && $i > 0; $i--) { $this->next(); @@ -1479,20 +1419,20 @@ public function skip($count = 1) /** * Format the date period as ISO 8601. - * - * @return string */ - public function toIso8601String() + public function toIso8601String(): string { $parts = []; - if ($this->recurrences !== null) { - $parts[] = 'R'.$this->recurrences; + if ($this->carbonRecurrences !== null) { + $parts[] = 'R'.$this->carbonRecurrences; } $parts[] = $this->startDate->toIso8601String(); - $parts[] = $this->dateInterval->spec(); + if (!$this->isDefaultInterval) { + $parts[] = $this->dateInterval->spec(); + } if ($this->endDate !== null) { $parts[] = $this->endDate->toIso8601String(); @@ -1503,12 +1443,12 @@ public function toIso8601String() /** * Convert the date period into a string. - * - * @return string */ - public function toString() + public function toString(): string { - $format = $this->localToStringFormat ?? static::$toStringFormat; + $format = $this->localToStringFormat + ?? $this->getFactory()->getSettings()['toStringFormat'] + ?? null; if ($format instanceof Closure) { return $format($this); @@ -1524,8 +1464,8 @@ public function toString() : 'Y-m-d' ); - if ($this->recurrences !== null) { - $parts[] = $this->translate('period_recurrences', [], $this->recurrences, $translator); + if ($this->carbonRecurrences !== null) { + $parts[] = $this->translate('period_recurrences', [], $this->carbonRecurrences, $translator); } $parts[] = $this->translate('period_interval', [':interval' => $this->dateInterval->forHumans([ @@ -1545,10 +1485,8 @@ public function toString() /** * Format the date period as ISO 8601. - * - * @return string */ - public function spec() + public function spec(): string { return $this->toIso8601String(); } @@ -1558,9 +1496,9 @@ public function spec() * * @param string $className The $className::instance() method will be called to cast the current object. * - * @return DatePeriod + * @return DatePeriod|object */ - public function cast(string $className) + public function cast(string $className): object { if (!method_exists($className, 'instance')) { if (is_a($className, DatePeriod::class, true)) { @@ -1568,7 +1506,7 @@ public function cast(string $className) $this->rawDate($this->getStartDate()), $this->getDateInterval(), $this->getEndDate() ? $this->rawDate($this->getIncludedEndDate()) : $this->getRecurrences(), - $this->isStartExcluded() ? DatePeriod::EXCLUDE_START_DATE : 0 + $this->isStartExcluded() ? DatePeriod::EXCLUDE_START_DATE : 0, ); } @@ -1585,10 +1523,8 @@ public function cast(string $className) * ``` * var_dump(CarbonPeriod::create('2021-01-05', '2021-02-15')->toDatePeriod()); * ``` - * - * @return DatePeriod */ - public function toDatePeriod() + public function toDatePeriod(): DatePeriod { return $this->cast(DatePeriod::class); } @@ -1605,7 +1541,7 @@ public function isUnfilteredAndEndLess(): bool foreach ($this->filters as $filter) { switch ($filter) { case [static::RECURRENCES_FILTER, null]: - if ($this->recurrences !== null && is_finite($this->recurrences)) { + if ($this->carbonRecurrences !== null && is_finite($this->carbonRecurrences)) { return false; } @@ -1631,7 +1567,7 @@ public function isUnfilteredAndEndLess(): bool * * @return CarbonInterface[] */ - public function toArray() + public function toArray(): array { if ($this->isUnfilteredAndEndLess()) { throw new EndLessPeriodException("Endless period can't be converted to array nor counted."); @@ -1639,34 +1575,29 @@ public function toArray() $state = [ $this->key, - $this->current ? $this->current->avoidMutation() : null, + $this->carbonCurrent ? $this->carbonCurrent->avoidMutation() : null, $this->validationResult, ]; $result = iterator_to_array($this); - [$this->key, $this->current, $this->validationResult] = $state; + [$this->key, $this->carbonCurrent, $this->validationResult] = $state; return $result; } /** * Count dates in the date period. - * - * @return int */ - #[ReturnTypeWillChange] - public function count() + public function count(): int { return \count($this->toArray()); } /** * Return the first date in the date period. - * - * @return CarbonInterface|null */ - public function first() + public function first(): ?CarbonInterface { if ($this->isUnfilteredAndEndLess()) { foreach ($this as $date) { @@ -1683,10 +1614,8 @@ public function first() /** * Return the last date in the date period. - * - * @return CarbonInterface|null */ - public function last() + public function last(): ?CarbonInterface { $array = $this->toArray(); @@ -1695,10 +1624,8 @@ public function last() /** * Convert the date period into a string. - * - * @return string */ - public function __toString() + public function __toString(): string { return $this->toString(); } @@ -1712,18 +1639,11 @@ public function __toString() * ->count() * * Note: We use magic method to let static and instance aliases with the same names. - * - * @param string $method - * @param array $parameters - * - * @return mixed */ - public function __call($method, $parameters) + public function __call(string $method, array $parameters): mixed { if (static::hasMacro($method)) { - return static::bindMacroContext($this, function () use (&$method, &$parameters) { - return $this->callMacro($method, $parameters); - }); + return static::bindMacroContext($this, fn () => $this->callMacro($method, $parameters)); } $roundedValue = $this->callRoundMethod($method, $parameters); @@ -1732,9 +1652,15 @@ public function __call($method, $parameters) return $roundedValue; } + $count = \count($parameters); + switch ($method) { case 'start': case 'since': + if ($count === 0) { + return $this->getStartDate(); + } + self::setDefaultParameters($parameters, [ [0, 'date', null], ]); @@ -1746,6 +1672,10 @@ public function __call($method, $parameters) case 'end': case 'until': + if ($count === 0) { + return $this->getEndDate(); + } + self::setDefaultParameters($parameters, [ [0, 'date', null], ]); @@ -1766,6 +1696,10 @@ public function __call($method, $parameters) case 'recurrences': case 'times': + if ($count === 0) { + return $this->getRecurrences(); + } + self::setDefaultParameters($parameters, [ [0, 'recurrences', null], ]); @@ -1773,6 +1707,10 @@ public function __call($method, $parameters) return $this->setRecurrences(...$parameters); case 'options': + if ($count === 0) { + return $this->getOptions(); + } + self::setDefaultParameters($parameters, [ [0, 'options', null], ]); @@ -1794,6 +1732,10 @@ public function __call($method, $parameters) return $this->prependFilter(...$parameters); case 'filters': + if ($count === 0) { + return $this->getFilters(); + } + self::setDefaultParameters($parameters, [ [0, 'filters', []], ]); @@ -1805,6 +1747,10 @@ public function __call($method, $parameters) case 'every': case 'step': case 'stepBy': + if ($count === 0) { + return $this->getDateInterval(); + } + return $this->setDateInterval(...$parameters); case 'invert': @@ -1846,16 +1792,12 @@ public function __call($method, $parameters) /** * Set the instance's timezone from a string or object and apply it to start/end. - * - * @param \DateTimeZone|string $timezone - * - * @return static */ - public function setTimezone($timezone) + public function setTimezone(DateTimeZone|string|int $timezone): static { $self = $this->copyIfImmutable(); - $self->tzName = $timezone; - $self->timezone = $timezone; + $self->timezoneSetting = $timezone; + $self->timezone = CarbonTimeZone::instance($timezone); if ($self->startDate) { $self = $self->setStartDate($self->startDate->setTimezone($timezone)); @@ -1870,16 +1812,12 @@ public function setTimezone($timezone) /** * Set the instance's timezone from a string or object and add/subtract the offset difference to start/end. - * - * @param \DateTimeZone|string $timezone - * - * @return static */ - public function shiftTimezone($timezone) + public function shiftTimezone(DateTimeZone|string|int $timezone): static { $self = $this->copyIfImmutable(); - $self->tzName = $timezone; - $self->timezone = $timezone; + $self->timezoneSetting = $timezone; + $self->timezone = CarbonTimeZone::instance($timezone); if ($self->startDate) { $self = $self->setStartDate($self->startDate->shiftTimezone($timezone)); @@ -1893,13 +1831,13 @@ public function shiftTimezone($timezone) } /** - * Returns the end is set, else calculated from start an recurrences. + * Returns the end is set, else calculated from start and recurrences. * * @param string|null $rounding Optional rounding 'floor', 'ceil', 'round' using the period interval. * * @return CarbonInterface */ - public function calculateEnd(string $rounding = null) + public function calculateEnd(?string $rounding = null): CarbonInterface { if ($end = $this->getEndDate($rounding)) { return $end; @@ -1918,19 +1856,16 @@ public function calculateEnd(string $rounding = null) return $date; } - /** - * @return CarbonInterface|null - */ - private function getEndFromRecurrences() + private function getEndFromRecurrences(): ?CarbonInterface { - if ($this->recurrences === null) { + if ($this->carbonRecurrences === null) { throw new UnreachableException( "Could not calculate period end without either explicit end or recurrences.\n". - "If you're looking for a forever-period, use ->setRecurrences(INF)." + "If you're looking for a forever-period, use ->setRecurrences(INF).", ); } - if ($this->recurrences === INF) { + if ($this->carbonRecurrences === INF) { $start = $this->getStartDate(); return $start < $start->avoidMutation()->add($this->getDateInterval()) @@ -1941,18 +1876,15 @@ private function getEndFromRecurrences() if ($this->filters === [[static::RECURRENCES_FILTER, null]]) { return $this->getStartDate()->avoidMutation()->add( $this->getDateInterval()->times( - $this->recurrences - ($this->isStartExcluded() ? 0 : 1) - ) + $this->carbonRecurrences - ($this->isStartExcluded() ? 0 : 1), + ), ); } return null; } - /** - * @return CarbonInterface|null - */ - private function iterateUntilEnd() + private function iterateUntilEnd(): ?CarbonInterface { $attempts = 0; $date = null; @@ -1960,7 +1892,7 @@ private function iterateUntilEnd() foreach ($this as $date) { if (++$attempts > static::END_MAX_ATTEMPTS) { throw new UnreachableException( - 'Could not calculate period end after iterating '.static::END_MAX_ATTEMPTS.' times.' + 'Could not calculate period end after iterating '.static::END_MAX_ATTEMPTS.' times.', ); } } @@ -1977,7 +1909,7 @@ private function iterateUntilEnd() * * @return bool */ - public function overlaps($rangeOrRangeStart, $rangeEnd = null) + public function overlaps(mixed $rangeOrRangeStart, mixed $rangeEnd = null): bool { $range = $rangeEnd ? static::create($rangeOrRangeStart, $rangeEnd) : $rangeOrRangeStart; @@ -2000,10 +1932,8 @@ public function overlaps($rangeOrRangeStart, $rangeEnd = null) * echo $date->diffInDays('2020-12-25')." days before Christmas!\n"; * }); * ``` - * - * @param callable $callback */ - public function forEach(callable $callback) + public function forEach(callable $callback): void { foreach ($this as $date) { $callback($date); @@ -2020,12 +1950,8 @@ public function forEach(callable $callback) * return $date->diffInDays('2020-12-25').' days before Christmas!'; * }))); * ``` - * - * @param callable $callback - * - * @return \Generator */ - public function map(callable $callback) + public function map(callable $callback): Generator { foreach ($this as $date) { yield $callback($date); @@ -2036,13 +1962,9 @@ public function map(callable $callback) * Determines if the instance is equal to another. * Warning: if options differ, instances will never be equal. * - * @param mixed $period - * * @see equalTo() - * - * @return bool */ - public function eq($period): bool + public function eq(mixed $period): bool { return $this->equalTo($period); } @@ -2050,12 +1972,8 @@ public function eq($period): bool /** * Determines if the instance is equal to another. * Warning: if options differ, instances will never be equal. - * - * @param mixed $period - * - * @return bool */ - public function equalTo($period): bool + public function equalTo(mixed $period): bool { if (!($period instanceof self)) { $period = self::make($period); @@ -2074,13 +1992,9 @@ public function equalTo($period): bool * Determines if the instance is not equal to another. * Warning: if options differ, instances will never be equal. * - * @param mixed $period - * * @see notEqualTo() - * - * @return bool */ - public function ne($period): bool + public function ne(mixed $period): bool { return $this->notEqualTo($period); } @@ -2088,25 +2002,17 @@ public function ne($period): bool /** * Determines if the instance is not equal to another. * Warning: if options differ, instances will never be equal. - * - * @param mixed $period - * - * @return bool */ - public function notEqualTo($period): bool + public function notEqualTo(mixed $period): bool { return !$this->eq($period); } /** - * Determines if the start date is before an other given date. + * Determines if the start date is before another given date. * (Rather start/end are included by options is ignored.) - * - * @param mixed $date - * - * @return bool */ - public function startsBefore($date = null): bool + public function startsBefore(mixed $date = null): bool { return $this->getStartDate()->lessThan($this->resolveCarbon($date)); } @@ -2114,25 +2020,17 @@ public function startsBefore($date = null): bool /** * Determines if the start date is before or the same as a given date. * (Rather start/end are included by options is ignored.) - * - * @param mixed $date - * - * @return bool */ - public function startsBeforeOrAt($date = null): bool + public function startsBeforeOrAt(mixed $date = null): bool { return $this->getStartDate()->lessThanOrEqualTo($this->resolveCarbon($date)); } /** - * Determines if the start date is after an other given date. + * Determines if the start date is after another given date. * (Rather start/end are included by options is ignored.) - * - * @param mixed $date - * - * @return bool */ - public function startsAfter($date = null): bool + public function startsAfter(mixed $date = null): bool { return $this->getStartDate()->greaterThan($this->resolveCarbon($date)); } @@ -2140,12 +2038,8 @@ public function startsAfter($date = null): bool /** * Determines if the start date is after or the same as a given date. * (Rather start/end are included by options is ignored.) - * - * @param mixed $date - * - * @return bool */ - public function startsAfterOrAt($date = null): bool + public function startsAfterOrAt(mixed $date = null): bool { return $this->getStartDate()->greaterThanOrEqualTo($this->resolveCarbon($date)); } @@ -2153,25 +2047,17 @@ public function startsAfterOrAt($date = null): bool /** * Determines if the start date is the same as a given date. * (Rather start/end are included by options is ignored.) - * - * @param mixed $date - * - * @return bool */ - public function startsAt($date = null): bool + public function startsAt(mixed $date = null): bool { return $this->getStartDate()->equalTo($this->resolveCarbon($date)); } /** - * Determines if the end date is before an other given date. + * Determines if the end date is before another given date. * (Rather start/end are included by options is ignored.) - * - * @param mixed $date - * - * @return bool */ - public function endsBefore($date = null): bool + public function endsBefore(mixed $date = null): bool { return $this->calculateEnd()->lessThan($this->resolveCarbon($date)); } @@ -2179,25 +2065,17 @@ public function endsBefore($date = null): bool /** * Determines if the end date is before or the same as a given date. * (Rather start/end are included by options is ignored.) - * - * @param mixed $date - * - * @return bool */ - public function endsBeforeOrAt($date = null): bool + public function endsBeforeOrAt(mixed $date = null): bool { return $this->calculateEnd()->lessThanOrEqualTo($this->resolveCarbon($date)); } /** - * Determines if the end date is after an other given date. + * Determines if the end date is after another given date. * (Rather start/end are included by options is ignored.) - * - * @param mixed $date - * - * @return bool */ - public function endsAfter($date = null): bool + public function endsAfter(mixed $date = null): bool { return $this->calculateEnd()->greaterThan($this->resolveCarbon($date)); } @@ -2205,12 +2083,8 @@ public function endsAfter($date = null): bool /** * Determines if the end date is after or the same as a given date. * (Rather start/end are included by options is ignored.) - * - * @param mixed $date - * - * @return bool */ - public function endsAfterOrAt($date = null): bool + public function endsAfterOrAt(mixed $date = null): bool { return $this->calculateEnd()->greaterThanOrEqualTo($this->resolveCarbon($date)); } @@ -2218,12 +2092,8 @@ public function endsAfterOrAt($date = null): bool /** * Determines if the end date is the same as a given date. * (Rather start/end are included by options is ignored.) - * - * @param mixed $date - * - * @return bool */ - public function endsAt($date = null): bool + public function endsAt(mixed $date = null): bool { return $this->calculateEnd()->equalTo($this->resolveCarbon($date)); } @@ -2231,8 +2101,6 @@ public function endsAt($date = null): bool /** * Return true if start date is now or later. * (Rather start/end are included by options is ignored.) - * - * @return bool */ public function isStarted(): bool { @@ -2242,8 +2110,6 @@ public function isStarted(): bool /** * Return true if end date is now or later. * (Rather start/end are included by options is ignored.) - * - * @return bool */ public function isEnded(): bool { @@ -2253,8 +2119,6 @@ public function isEnded(): bool /** * Return true if now is between start date (included) and end date (excluded). * (Rather start/end are included by options is ignored.) - * - * @return bool */ public function isInProgress(): bool { @@ -2263,15 +2127,12 @@ public function isInProgress(): bool /** * Round the current instance at the given unit with given precision if specified and the given function. - * - * @param string $unit - * @param float|int|string|\DateInterval|null $precision - * @param string $function - * - * @return static */ - public function roundUnit($unit, $precision = 1, $function = 'round') - { + public function roundUnit( + string $unit, + DateInterval|float|int|string|null $precision = 1, + callable|string $function = 'round', + ): static { $self = $this->copyIfImmutable(); $self = $self->setStartDate($self->getStartDate()->roundUnit($unit, $precision, $function)); @@ -2284,40 +2145,27 @@ public function roundUnit($unit, $precision = 1, $function = 'round') /** * Truncate the current instance at the given unit with given precision if specified. - * - * @param string $unit - * @param float|int|string|\DateInterval|null $precision - * - * @return static */ - public function floorUnit($unit, $precision = 1) + public function floorUnit(string $unit, DateInterval|float|int|string|null $precision = 1): static { return $this->roundUnit($unit, $precision, 'floor'); } /** * Ceil the current instance at the given unit with given precision if specified. - * - * @param string $unit - * @param float|int|string|\DateInterval|null $precision - * - * @return static */ - public function ceilUnit($unit, $precision = 1) + public function ceilUnit(string $unit, DateInterval|float|int|string|null $precision = 1): static { return $this->roundUnit($unit, $precision, 'ceil'); } /** * Round the current instance second with given precision if specified (else period interval is used). - * - * @param float|int|string|\DateInterval|null $precision - * @param string $function - * - * @return static */ - public function round($precision = null, $function = 'round') - { + public function round( + DateInterval|float|int|string|null $precision = null, + callable|string $function = 'round', + ): static { return $this->roundWith( $precision ?? $this->getDateInterval()->setLocalTranslator(TranslatorImmutable::get('en'))->forHumans(), $function @@ -2326,24 +2174,16 @@ public function round($precision = null, $function = 'round') /** * Round the current instance second with given precision if specified (else period interval is used). - * - * @param float|int|string|\DateInterval|null $precision - * - * @return static */ - public function floor($precision = null) + public function floor(DateInterval|float|int|string|null $precision = null): static { return $this->round($precision, 'floor'); } /** * Ceil the current instance second with given precision if specified (else period interval is used). - * - * @param float|int|string|\DateInterval|null $precision - * - * @return static */ - public function ceil($precision = null) + public function ceil(DateInterval|float|int|string|null $precision = null): static { return $this->round($precision, 'ceil'); } @@ -2355,20 +2195,15 @@ public function ceil($precision = null) * * @return CarbonInterface[] */ - #[ReturnTypeWillChange] - public function jsonSerialize() + public function jsonSerialize(): array { return $this->toArray(); } /** * Return true if the given date is between start and end. - * - * @param \Carbon\Carbon|\Carbon\CarbonPeriod|\Carbon\CarbonInterval|\DateInterval|\DatePeriod|\DateTimeInterface|string|null $date - * - * @return bool */ - public function contains($date = null): bool + public function contains(mixed $date = null): bool { $startMethod = 'startsBefore'.($this->isStartIncluded() ? 'OrAt' : ''); $endMethod = 'endsAfter'.($this->isEndIncluded() ? 'OrAt' : ''); @@ -2380,12 +2215,8 @@ public function contains($date = null): bool * Return true if the current period follows a given other period (with no overlap). * For instance, [2019-08-01 -> 2019-08-12] follows [2019-07-29 -> 2019-07-31] * Note than in this example, follows() would be false if 2019-08-01 or 2019-07-31 was excluded by options. - * - * @param \Carbon\CarbonPeriod|\DatePeriod|string $period - * - * @return bool */ - public function follows($period, ...$arguments): bool + public function follows(mixed $period, mixed ...$arguments): bool { $period = $this->resolveCarbonPeriod($period, ...$arguments); @@ -2396,12 +2227,8 @@ public function follows($period, ...$arguments): bool * Return true if the given other period follows the current one (with no overlap). * For instance, [2019-07-29 -> 2019-07-31] is followed by [2019-08-01 -> 2019-08-12] * Note than in this example, isFollowedBy() would be false if 2019-08-01 or 2019-07-31 was excluded by options. - * - * @param \Carbon\CarbonPeriod|\DatePeriod|string $period - * - * @return bool */ - public function isFollowedBy($period, ...$arguments): bool + public function isFollowedBy(mixed $period, mixed ...$arguments): bool { $period = $this->resolveCarbonPeriod($period, ...$arguments); @@ -2413,29 +2240,120 @@ public function isFollowedBy($period, ...$arguments): bool * * @see follows() * @see isFollowedBy() - * - * @param \Carbon\CarbonPeriod|\DatePeriod|string $period - * - * @return bool */ - public function isConsecutiveWith($period, ...$arguments): bool + public function isConsecutiveWith(mixed $period, mixed ...$arguments): bool { return $this->follows($period, ...$arguments) || $this->isFollowedBy($period, ...$arguments); } + public function __debugInfo(): array + { + $info = $this->baseDebugInfo(); + unset( + $info['start'], + $info['end'], + $info['interval'], + $info['include_start_date'], + $info['include_end_date'], + $info['constructed'], + $info["\0*\0constructed"], + ); + + return $info; + } + + public function __unserialize(array $data): void + { + try { + $values = array_combine( + array_map( + static fn (string $key): string => preg_replace('/^\0\*\0/', '', $key), + array_keys($data), + ), + $data, + ); + + $this->initializeSerialization($values); + + foreach ($values as $key => $value) { + if ($value === null) { + continue; + } + + $property = match ($key) { + 'tzName' => $this->setTimezone(...), + 'options' => $this->setOptions(...), + 'recurrences' => $this->setRecurrences(...), + 'current' => function (mixed $current): void { + if (!($current instanceof CarbonInterface)) { + $current = $this->resolveCarbon($current); + } + + $this->carbonCurrent = $current; + }, + 'start' => 'startDate', + 'interval' => $this->setDateInterval(...), + 'end' => 'endDate', + 'key' => null, + 'include_start_date' => function (bool $included): void { + $this->excludeStartDate(!$included); + }, + 'include_end_date' => function (bool $included): void { + $this->excludeEndDate(!$included); + }, + default => $key, + }; + + if ($property === null) { + continue; + } + + if (\is_callable($property)) { + $property($value); + + continue; + } + + if ($value instanceof DateTimeInterface && !($value instanceof CarbonInterface)) { + $value = ($value instanceof DateTime) + ? Carbon::instance($value) + : CarbonImmutable::instance($value); + } + + try { + $this->$property = $value; + } catch (Throwable) { + // Must be ignored for backward-compatibility + } + } + + if (\array_key_exists('carbonRecurrences', $values)) { + $this->carbonRecurrences = $values['carbonRecurrences']; + } elseif (((int) ($values['recurrences'] ?? 0)) <= 1 && $this->endDate !== null) { + $this->carbonRecurrences = null; + } + } catch (Throwable $e) { + // @codeCoverageIgnoreStart + if (!method_exists(parent::class, '__unserialize')) { + throw $e; + } + + parent::__unserialize($data); + // @codeCoverageIgnoreEnd + } + } + /** * Update properties after removing built-in filters. - * - * @return void */ - protected function updateInternalState() + protected function updateInternalState(): void { if (!$this->hasFilter(static::END_DATE_FILTER)) { $this->endDate = null; } if (!$this->hasFilter(static::RECURRENCES_FILTER)) { - $this->recurrences = null; + $this->carbonRecurrences = null; } } @@ -2443,12 +2361,8 @@ protected function updateInternalState() * Create a filter tuple from raw parameters. * * Will create an automatic filter callback for one of Carbon's is* methods. - * - * @param array $parameters - * - * @return array */ - protected function createFilterTuple(array $parameters) + protected function createFilterTuple(array $parameters): array { $method = array_shift($parameters); @@ -2456,20 +2370,14 @@ protected function createFilterTuple(array $parameters) return [$method, array_shift($parameters)]; } - return [function ($date) use ($method, $parameters) { - return ([$date, $method])(...$parameters); - }, $method]; + return [static fn ($date) => ([$date, $method])(...$parameters), $method]; } /** * Return whether given callable is a string pointing to one of Carbon's is* methods * and should be automatically converted to a filter callback. - * - * @param callable $callable - * - * @return bool */ - protected function isCarbonPredicateMethod($callable) + protected function isCarbonPredicateMethod(callable|string $callable): bool { return \is_string($callable) && str_starts_with($callable, 'is') && (method_exists($this->dateClass, $callable) || ([$this->dateClass, 'hasMacro'])($callable)); @@ -2478,16 +2386,11 @@ protected function isCarbonPredicateMethod($callable) /** * Recurrences filter callback (limits number of recurrences). * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * - * @param \Carbon\Carbon $current - * @param int $key - * - * @return bool|string + * @SuppressWarnings(UnusedFormalParameter) */ - protected function filterRecurrences($current, $key) + protected function filterRecurrences(CarbonInterface $current, int $key): bool|callable { - if ($key < $this->recurrences) { + if ($key < $this->carbonRecurrences) { return true; } @@ -2497,11 +2400,9 @@ protected function filterRecurrences($current, $key) /** * End date filter callback. * - * @param \Carbon\Carbon $current - * - * @return bool|string + * @return bool|static::END_ITERATION */ - protected function filterEndDate($current) + protected function filterEndDate(CarbonInterface $current): bool|callable { if (!$this->isEndExcluded() && $current == $this->endDate) { return true; @@ -2517,9 +2418,9 @@ protected function filterEndDate($current) /** * End iteration filter callback. * - * @return string + * @return static::END_ITERATION */ - protected function endIteration() + protected function endIteration(): callable { return static::END_ITERATION; } @@ -2527,7 +2428,7 @@ protected function endIteration() /** * Handle change of the parameters. */ - protected function handleChangedParameters() + protected function handleChangedParameters(): void { if (($this->getOptions() & static::IMMUTABLE) && $this->dateClass === Carbon::class) { $this->dateClass = CarbonImmutable::class; @@ -2544,11 +2445,11 @@ protected function handleChangedParameters() * Returns true when current date is valid, false if it is not, or static::END_ITERATION * when iteration should be stopped. * - * @return bool|string + * @return bool|static::END_ITERATION */ - protected function validateCurrentDate() + protected function validateCurrentDate(): bool|callable { - if ($this->current === null) { + if ($this->carbonCurrent === null) { $this->rewind(); } @@ -2559,19 +2460,14 @@ protected function validateCurrentDate() /** * Check whether current value and key pass all the filters. * - * @return bool|string + * @return bool|static::END_ITERATION */ - protected function checkFilters() + protected function checkFilters(): bool|callable { - $current = $this->prepareForReturn($this->current); + $current = $this->prepareForReturn($this->carbonCurrent); foreach ($this->filters as $tuple) { - $result = \call_user_func( - $tuple[0], - $current->avoidMutation(), - $this->key, - $this - ); + $result = \call_user_func($tuple[0], $current->avoidMutation(), $this->key, $this); if ($result === static::END_ITERATION) { return static::END_ITERATION; @@ -2597,7 +2493,7 @@ protected function prepareForReturn(CarbonInterface $date) $date = ([$this->dateClass, 'make'])($date); if ($this->timezone) { - $date = $date->setTimezone($this->timezone); + return $date->setTimezone($this->timezone); } return $date; @@ -2607,15 +2503,13 @@ protected function prepareForReturn(CarbonInterface $date) * Keep incrementing the current date until a valid date is found or the iteration is ended. * * @throws RuntimeException - * - * @return void */ - protected function incrementCurrentDateUntilValid() + protected function incrementCurrentDateUntilValid(): void { $attempts = 0; do { - $this->current = $this->current->add($this->dateInterval); + $this->carbonCurrent = $this->carbonCurrent->add($this->dateInterval); $this->validationResult = null; @@ -2627,13 +2521,8 @@ protected function incrementCurrentDateUntilValid() /** * Call given macro. - * - * @param string $name - * @param array $parameters - * - * @return mixed */ - protected function callMacro($name, $parameters) + protected function callMacro(string $name, array $parameters): mixed { $macro = static::$macros[$name]; @@ -2661,13 +2550,8 @@ protected function resolveCarbon($date = null) /** * Resolve passed arguments or DatePeriod to a CarbonPeriod object. - * - * @param mixed $period - * @param mixed ...$arguments - * - * @return static */ - protected function resolveCarbonPeriod($period, ...$arguments) + protected function resolveCarbonPeriod(mixed $period, mixed ...$arguments): self { if ($period instanceof self) { return $period; @@ -2689,6 +2573,12 @@ private function makeDateTime($value): ?DateTimeInterface return $value; } + if ($value instanceof WeekDay || $value instanceof Month) { + $dateClass = $this->dateClass; + + return new $dateClass($value, $this->timezoneSetting); + } + if (\is_string($value)) { $value = trim($value); @@ -2698,7 +2588,7 @@ private function makeDateTime($value): ?DateTimeInterface ) { $dateClass = $this->dateClass; - return $dateClass::parse($value, $this->tzName); + return $dateClass::parse($value, $this->timezoneSetting); } } @@ -2739,4 +2629,89 @@ private static function setDefaultParameters(array &$parameters, array $defaults } } } + + private function setFromAssociativeArray(array $parameters): void + { + if (isset($parameters['start'])) { + $this->setStartDate($parameters['start']); + } + + if (isset($parameters['start'])) { + $this->setStartDate($parameters['start']); + } + + if (isset($parameters['end'])) { + $this->setEndDate($parameters['end']); + } + + if (isset($parameters['recurrences'])) { + $this->setRecurrences($parameters['recurrences']); + } + + if (isset($parameters['interval'])) { + $this->setDateInterval($parameters['interval']); + } + + if (isset($parameters['options'])) { + $this->setOptions($parameters['options']); + } + } + + private function configureTimezone(DateTimeZone $timezone, array $sortedArguments, array $originalArguments): array + { + $this->setTimezone($timezone); + + if (\is_string($originalArguments['start'] ?? null)) { + $sortedArguments['start'] = $this->makeDateTime($originalArguments['start']); + } + + if (\is_string($originalArguments['end'] ?? null)) { + $sortedArguments['end'] = $this->makeDateTime($originalArguments['end']); + } + + return $sortedArguments; + } + + private function initializeSerialization(array $values): void + { + $serializationBase = [ + 'start' => $values['start'] ?? $values['startDate'] ?? null, + 'current' => $values['current'] ?? $values['carbonCurrent'] ?? null, + 'end' => $values['end'] ?? $values['endDate'] ?? null, + 'interval' => $values['interval'] ?? $values['dateInterval'] ?? null, + 'recurrences' => max(1, (int) ($values['recurrences'] ?? $values['carbonRecurrences'] ?? 1)), + 'include_start_date' => $values['include_start_date'] ?? true, + 'include_end_date' => $values['include_end_date'] ?? false, + ]; + + foreach (['start', 'current', 'end'] as $dateProperty) { + if ($serializationBase[$dateProperty] instanceof Carbon) { + $serializationBase[$dateProperty] = $serializationBase[$dateProperty]->toDateTime(); + } elseif ($serializationBase[$dateProperty] instanceof CarbonInterface) { + $serializationBase[$dateProperty] = $serializationBase[$dateProperty]->toDateTimeImmutable(); + } + } + + if ($serializationBase['interval'] instanceof CarbonInterval) { + $serializationBase['interval'] = $serializationBase['interval']->toDateInterval(); + } + + // @codeCoverageIgnoreStart + if (method_exists(parent::class, '__unserialize')) { + parent::__unserialize($serializationBase); + + return; + } + + $excludeStart = !($values['include_start_date'] ?? true); + $includeEnd = $values['include_end_date'] ?? true; + + parent::__construct( + $serializationBase['start'], + $serializationBase['interval'], + $serializationBase['end'] ?? $serializationBase['recurrences'], + ($excludeStart ? self::EXCLUDE_START_DATE : 0) | ($includeEnd && \defined('DatePeriod::INCLUDE_END_DATE') ? self::INCLUDE_END_DATE : 0), + ); + // @codeCoverageIgnoreEnd + } } diff --git a/vendor/nesbot/carbon/src/Carbon/CarbonPeriodImmutable.php b/vendor/nesbot/carbon/src/Carbon/CarbonPeriodImmutable.php index f0d0ee281..0e8ff28e2 100644 --- a/vendor/nesbot/carbon/src/Carbon/CarbonPeriodImmutable.php +++ b/vendor/nesbot/carbon/src/Carbon/CarbonPeriodImmutable.php @@ -1,5 +1,7 @@ constructed ? clone $this : $this; } diff --git a/vendor/nesbot/carbon/src/Carbon/CarbonTimeZone.php b/vendor/nesbot/carbon/src/Carbon/CarbonTimeZone.php index c81899f1e..45a46d171 100644 --- a/vendor/nesbot/carbon/src/Carbon/CarbonTimeZone.php +++ b/vendor/nesbot/carbon/src/Carbon/CarbonTimeZone.php @@ -1,5 +1,7 @@ initLocalFactory(); + parent::__construct(static::getDateTimeZoneNameFromMixed($timezone)); } - protected static function parseNumericTimezone($timezone) + protected static function parseNumericTimezone(string|int|float $timezone): string { - if ($timezone <= -100 || $timezone >= 100) { - throw new InvalidTimeZoneException('Absolute timezone offset cannot be greater than 100.'); + if (abs((float) $timezone) > static::MAXIMUM_TIMEZONE_OFFSET) { + throw new InvalidTimeZoneException( + 'Absolute timezone offset cannot be greater than '. + static::MAXIMUM_TIMEZONE_OFFSET.'.', + ); } - return ($timezone >= 0 ? '+' : '').ltrim($timezone, '+').':00'; + return ($timezone >= 0 ? '+' : '').ltrim((string) $timezone, '+').':00'; } - protected static function getDateTimeZoneNameFromMixed($timezone) + protected static function getDateTimeZoneNameFromMixed(string|int|float $timezone): string { - if ($timezone === null) { - return date_default_timezone_get(); - } - if (\is_string($timezone)) { $timezone = preg_replace('/^\s*([+-]\d+)(\d{2})\s*$/', '$1:$2', $timezone); } @@ -50,19 +60,14 @@ protected static function getDateTimeZoneNameFromMixed($timezone) return $timezone; } - protected static function getDateTimeZoneFromName(&$name) - { - return @timezone_open($name = (string) static::getDateTimeZoneNameFromMixed($name)); - } - /** * Cast the current instance into the given class. * - * @param string $className The $className::instance() method will be called to cast the current object. + * @param class-string $className The $className::instance() method will be called to cast the current object. * - * @return DateTimeZone + * @return DateTimeZone|mixed */ - public function cast(string $className) + public function cast(string $className): mixed { if (!method_exists($className, 'instance')) { if (is_a($className, DateTimeZone::class, true)) { @@ -78,38 +83,40 @@ public function cast(string $className) /** * Create a CarbonTimeZone from mixed input. * - * @param DateTimeZone|string|int|null $object original value to get CarbonTimeZone from it. - * @param DateTimeZone|string|int|null $objectDump dump of the object for error messages. + * @param DateTimeZone|string|int|false|null $object original value to get CarbonTimeZone from it. + * @param DateTimeZone|string|int|false|null $objectDump dump of the object for error messages. * * @throws InvalidTimeZoneException * - * @return false|static + * @return static|null */ - public static function instance($object = null, $objectDump = null) - { - $tz = $object; - - if ($tz instanceof static) { - return $tz; - } - - if ($tz === null) { - return new static(); + public static function instance( + DateTimeZone|string|int|false|null $object, + DateTimeZone|string|int|false|null $objectDump = null, + ): ?self { + $timezone = $object; + + if ($timezone instanceof static) { + return $timezone; } - if (!$tz instanceof DateTimeZone) { - $tz = static::getDateTimeZoneFromName($object); + if ($timezone === null || $timezone === false) { + return null; } - if ($tz !== false) { - return new static($tz->getName()); - } + try { + if (!($timezone instanceof DateTimeZone)) { + $name = static::getDateTimeZoneNameFromMixed($object); + $timezone = new static($name); + } - if (Carbon::isStrictModeEnabled()) { - throw new InvalidTimeZoneException('Unknown or bad timezone ('.($objectDump ?: $object).')'); + return $timezone instanceof static ? $timezone : new static($timezone->getName()); + } catch (Exception $exception) { + throw new InvalidTimeZoneException( + 'Unknown or bad timezone ('.($objectDump ?: $object).')', + previous: $exception, + ); } - - return false; } /** @@ -119,14 +126,27 @@ public static function instance($object = null, $objectDump = null) * * @return string */ - public function getAbbreviatedName($dst = false) + public function getAbbreviatedName(bool $dst = false): string { $name = $this->getName(); - foreach ($this->listAbbreviations() as $abbreviation => $zones) { + $date = new DateTimeImmutable($dst ? 'July 1' : 'January 1', $this); + $timezone = $date->format('T'); + $abbreviations = $this->listAbbreviations(); + $matchingZones = array_merge($abbreviations[$timezone] ?? [], $abbreviations[strtolower($timezone)] ?? []); + + if ($matchingZones !== []) { + foreach ($matchingZones as $zone) { + if ($zone['timezone_id'] === $name && $zone['dst'] == $dst) { + return $timezone; + } + } + } + + foreach ($abbreviations as $abbreviation => $zones) { foreach ($zones as $zone) { if ($zone['timezone_id'] === $name && $zone['dst'] == $dst) { - return $abbreviation; + return strtoupper($abbreviation); } } } @@ -143,33 +163,25 @@ public function getAbbreviatedName($dst = false) * * @return string */ - public function getAbbr($dst = false) + public function getAbbr(bool $dst = false): string { return $this->getAbbreviatedName($dst); } /** * Get the offset as string "sHH:MM" (such as "+00:00" or "-12:30"). - * - * @param DateTimeInterface|null $date - * - * @return string */ - public function toOffsetName(DateTimeInterface $date = null) + public function toOffsetName(?DateTimeInterface $date = null): string { return static::getOffsetNameFromMinuteOffset( - $this->getOffset($date ?: Carbon::now($this)) / 60 + $this->getOffset($this->resolveCarbon($date)) / 60, ); } /** * Returns a new CarbonTimeZone object using the offset string instead of region string. - * - * @param DateTimeInterface|null $date - * - * @return CarbonTimeZone */ - public function toOffsetTimeZone(DateTimeInterface $date = null) + public function toOffsetTimeZone(?DateTimeInterface $date = null): static { return new static($this->toOffsetName($date)); } @@ -179,13 +191,8 @@ public function toOffsetTimeZone(DateTimeInterface $date = null) * false if no match is found. * * @see timezone_name_from_abbr native PHP function. - * - * @param DateTimeInterface|null $date - * @param int $isDst - * - * @return string|false */ - public function toRegionName(DateTimeInterface $date = null, $isDst = 1) + public function toRegionName(?DateTimeInterface $date = null, int $isDST = 1): ?string { $name = $this->getName(); $firstChar = substr($name, 0, 1); @@ -194,52 +201,48 @@ public function toRegionName(DateTimeInterface $date = null, $isDst = 1) return $name; } - $date = $date ?: Carbon::now($this); + $date = $this->resolveCarbon($date); // Integer construction no longer supported since PHP 8 // @codeCoverageIgnoreStart try { $offset = @$this->getOffset($date) ?: 0; - } catch (Throwable $e) { + } catch (Throwable) { $offset = 0; } // @codeCoverageIgnoreEnd - $name = @timezone_name_from_abbr('', $offset, $isDst); + $name = @timezone_name_from_abbr('', $offset, $isDST); if ($name) { return $name; } foreach (timezone_identifiers_list() as $timezone) { - if (Carbon::instance($date)->tz($timezone)->getOffset() === $offset) { + if (Carbon::instance($date)->setTimezone($timezone)->getOffset() === $offset) { return $timezone; } } - return false; + return null; } /** * Returns a new CarbonTimeZone object using the region string instead of offset string. - * - * @param DateTimeInterface|null $date - * - * @return CarbonTimeZone|false */ - public function toRegionTimeZone(DateTimeInterface $date = null) + public function toRegionTimeZone(?DateTimeInterface $date = null): ?self { - $tz = $this->toRegionName($date); + $timezone = $this->toRegionName($date); - if ($tz !== false) { - return new static($tz); + if ($timezone !== null) { + return new static($timezone); } if (Carbon::isStrictModeEnabled()) { - throw new InvalidTimeZoneException('Unknown timezone for offset '.$this->getOffset($date ?: Carbon::now($this)).' seconds.'); + throw new InvalidTimeZoneException('Unknown timezone for offset '.$this->getOffset($this->resolveCarbon($date)).' seconds.'); } - return false; + return null; } /** @@ -317,4 +320,17 @@ public static function getOffsetNameFromMinuteOffset(float $minutes): string ':'. str_pad((string) ($unsignedMinutes % 60), 2, '0', STR_PAD_LEFT); } + + private function resolveCarbon(?DateTimeInterface $date): DateTimeInterface + { + if ($date) { + return $date; + } + + if (isset($this->clock)) { + return $this->clock->now()->setTimezone($this); + } + + return Carbon::now($this); + } } diff --git a/vendor/nesbot/carbon/src/Carbon/Cli/Invoker.php b/vendor/nesbot/carbon/src/Carbon/Cli/Invoker.php index 4f35d6c61..3aabe3f63 100644 --- a/vendor/nesbot/carbon/src/Carbon/Cli/Invoker.php +++ b/vendor/nesbot/carbon/src/Carbon/Cli/Invoker.php @@ -1,5 +1,7 @@ - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Carbon\Doctrine; - -use Doctrine\DBAL\Platforms\AbstractPlatform; - -interface CarbonDoctrineType -{ - public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform); - - public function convertToPHPValue($value, AbstractPlatform $platform); - - public function convertToDatabaseValue($value, AbstractPlatform $platform); -} diff --git a/vendor/nesbot/carbon/src/Carbon/Doctrine/CarbonImmutableType.php b/vendor/nesbot/carbon/src/Carbon/Doctrine/CarbonImmutableType.php deleted file mode 100644 index bf476a77e..000000000 --- a/vendor/nesbot/carbon/src/Carbon/Doctrine/CarbonImmutableType.php +++ /dev/null @@ -1,37 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Carbon\Doctrine; - -use Doctrine\DBAL\Platforms\AbstractPlatform; - -class CarbonImmutableType extends DateTimeImmutableType implements CarbonDoctrineType -{ - /** - * {@inheritdoc} - * - * @return string - */ - public function getName() - { - return 'carbon_immutable'; - } - - /** - * {@inheritdoc} - * - * @return bool - */ - public function requiresSQLCommentHint(AbstractPlatform $platform) - { - return true; - } -} diff --git a/vendor/nesbot/carbon/src/Carbon/Doctrine/CarbonType.php b/vendor/nesbot/carbon/src/Carbon/Doctrine/CarbonType.php deleted file mode 100644 index 9289d84d3..000000000 --- a/vendor/nesbot/carbon/src/Carbon/Doctrine/CarbonType.php +++ /dev/null @@ -1,37 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Carbon\Doctrine; - -use Doctrine\DBAL\Platforms\AbstractPlatform; - -class CarbonType extends DateTimeType implements CarbonDoctrineType -{ - /** - * {@inheritdoc} - * - * @return string - */ - public function getName() - { - return 'carbon'; - } - - /** - * {@inheritdoc} - * - * @return bool - */ - public function requiresSQLCommentHint(AbstractPlatform $platform) - { - return true; - } -} diff --git a/vendor/nesbot/carbon/src/Carbon/Doctrine/CarbonTypeConverter.php b/vendor/nesbot/carbon/src/Carbon/Doctrine/CarbonTypeConverter.php deleted file mode 100644 index ecfe17e79..000000000 --- a/vendor/nesbot/carbon/src/Carbon/Doctrine/CarbonTypeConverter.php +++ /dev/null @@ -1,123 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Carbon\Doctrine; - -use Carbon\Carbon; -use Carbon\CarbonInterface; -use DateTimeInterface; -use Doctrine\DBAL\Platforms\AbstractPlatform; -use Doctrine\DBAL\Types\ConversionException; -use Exception; - -/** - * @template T of CarbonInterface - */ -trait CarbonTypeConverter -{ - /** - * @return class-string - */ - protected function getCarbonClassName(): string - { - return Carbon::class; - } - - /** - * @return string - */ - public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) - { - $precision = $fieldDeclaration['precision'] ?: 10; - - if ($fieldDeclaration['secondPrecision'] ?? false) { - $precision = 0; - } - - if ($precision === 10) { - $precision = DateTimeDefaultPrecision::get(); - } - - $type = parent::getSQLDeclaration($fieldDeclaration, $platform); - - if (!$precision) { - return $type; - } - - if (str_contains($type, '(')) { - return preg_replace('/\(\d+\)/', "($precision)", $type); - } - - [$before, $after] = explode(' ', "$type "); - - return trim("$before($precision) $after"); - } - - /** - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * - * @return T|null - */ - public function convertToPHPValue($value, AbstractPlatform $platform) - { - $class = $this->getCarbonClassName(); - - if ($value === null || is_a($value, $class)) { - return $value; - } - - if ($value instanceof DateTimeInterface) { - return $class::instance($value); - } - - $date = null; - $error = null; - - try { - $date = $class::parse($value); - } catch (Exception $exception) { - $error = $exception; - } - - if (!$date) { - throw ConversionException::conversionFailedFormat( - $value, - $this->getName(), - 'Y-m-d H:i:s.u or any format supported by '.$class.'::parse()', - $error - ); - } - - return $date; - } - - /** - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * - * @return string|null - */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) - { - if ($value === null) { - return $value; - } - - if ($value instanceof DateTimeInterface) { - return $value->format('Y-m-d H:i:s.u'); - } - - throw ConversionException::conversionFailedInvalidType( - $value, - $this->getName(), - ['null', 'DateTime', 'Carbon'] - ); - } -} diff --git a/vendor/nesbot/carbon/src/Carbon/Doctrine/DateTimeDefaultPrecision.php b/vendor/nesbot/carbon/src/Carbon/Doctrine/DateTimeDefaultPrecision.php deleted file mode 100644 index 642fd4135..000000000 --- a/vendor/nesbot/carbon/src/Carbon/Doctrine/DateTimeDefaultPrecision.php +++ /dev/null @@ -1,37 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Carbon\Doctrine; - -class DateTimeDefaultPrecision -{ - private static $precision = 6; - - /** - * Change the default Doctrine datetime and datetime_immutable precision. - * - * @param int $precision - */ - public static function set(int $precision): void - { - self::$precision = $precision; - } - - /** - * Get the default Doctrine datetime and datetime_immutable precision. - * - * @return int - */ - public static function get(): int - { - return self::$precision; - } -} diff --git a/vendor/nesbot/carbon/src/Carbon/Doctrine/DateTimeImmutableType.php b/vendor/nesbot/carbon/src/Carbon/Doctrine/DateTimeImmutableType.php deleted file mode 100644 index 499271031..000000000 --- a/vendor/nesbot/carbon/src/Carbon/Doctrine/DateTimeImmutableType.php +++ /dev/null @@ -1,24 +0,0 @@ - */ - use CarbonTypeConverter; - - /** - * @return class-string - */ - protected function getCarbonClassName(): string - { - return CarbonImmutable::class; - } -} diff --git a/vendor/nesbot/carbon/src/Carbon/Doctrine/DateTimeType.php b/vendor/nesbot/carbon/src/Carbon/Doctrine/DateTimeType.php deleted file mode 100644 index 29b0bb955..000000000 --- a/vendor/nesbot/carbon/src/Carbon/Doctrine/DateTimeType.php +++ /dev/null @@ -1,16 +0,0 @@ - */ - use CarbonTypeConverter; -} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/BadComparisonUnitException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/BadComparisonUnitException.php index 3ca8837d1..db1ea8e99 100644 --- a/vendor/nesbot/carbon/src/Carbon/Exceptions/BadComparisonUnitException.php +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/BadComparisonUnitException.php @@ -1,5 +1,7 @@ unit = $unit; diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/BadFluentConstructorException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/BadFluentConstructorException.php index 2e222e54e..e8cded033 100644 --- a/vendor/nesbot/carbon/src/Carbon/Exceptions/BadFluentConstructorException.php +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/BadFluentConstructorException.php @@ -1,5 +1,7 @@ method = $method; - parent::__construct(sprintf("Unknown fluent constructor '%s'.", $method), $code, $previous); + parent::__construct(\sprintf("Unknown fluent constructor '%s'.", $method), $code, $previous); } /** diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/BadFluentSetterException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/BadFluentSetterException.php index 4ceaa2ef0..b3111bff0 100644 --- a/vendor/nesbot/carbon/src/Carbon/Exceptions/BadFluentSetterException.php +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/BadFluentSetterException.php @@ -1,5 +1,7 @@ setter = $setter; - parent::__construct(sprintf("Unknown fluent setter '%s'", $setter), $code, $previous); + parent::__construct(\sprintf("Unknown fluent setter '%s'", $setter), $code, $previous); } /** diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/BadMethodCallException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/BadMethodCallException.php index 108206d3e..3f10af992 100644 --- a/vendor/nesbot/carbon/src/Carbon/Exceptions/BadMethodCallException.php +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/BadMethodCallException.php @@ -1,5 +1,7 @@ value = $value; parent::__construct("$value is immutable.", $code, $previous); diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidArgumentException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidArgumentException.php index 5b013cd50..7fb5a991d 100644 --- a/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidArgumentException.php +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidArgumentException.php @@ -1,5 +1,7 @@ field = $field; $this->value = $value; diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidFormatException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidFormatException.php index 92d55fe35..ffc5f212f 100644 --- a/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidFormatException.php +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidFormatException.php @@ -1,5 +1,7 @@ className = $className; - parent::__construct(sprintf('Given class does not implement %s: %s', CarbonInterface::class, $className), $code, $previous); + parent::__construct(\sprintf( + 'Given class does not implement %s: %s', + CarbonInterface::class, + $className, + ), $code, $previous); } /** diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/NotAPeriodException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/NotAPeriodException.php index 4edd7a484..23e09a286 100644 --- a/vendor/nesbot/carbon/src/Carbon/Exceptions/NotAPeriodException.php +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/NotAPeriodException.php @@ -1,5 +1,7 @@ unit = $unit; $this->min = $min; diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/ParseErrorException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/ParseErrorException.php index 5416fd149..556bfedeb 100644 --- a/vendor/nesbot/carbon/src/Carbon/Exceptions/ParseErrorException.php +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/ParseErrorException.php @@ -1,5 +1,7 @@ expected = $expected; $this->actual = $actual; diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/RuntimeException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/RuntimeException.php index ad196f79d..85bfb142b 100644 --- a/vendor/nesbot/carbon/src/Carbon/Exceptions/RuntimeException.php +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/RuntimeException.php @@ -1,5 +1,7 @@ unit = $unit; diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownGetterException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownGetterException.php index 5c504975a..982a30820 100644 --- a/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownGetterException.php +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownGetterException.php @@ -1,5 +1,7 @@ getter = $getter; diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownMethodException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownMethodException.php index 75273a706..c72c368cf 100644 --- a/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownMethodException.php +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownMethodException.php @@ -1,5 +1,7 @@ method = $method; diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownSetterException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownSetterException.php index a795f5d72..e97db4bb2 100644 --- a/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownSetterException.php +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownSetterException.php @@ -1,5 +1,7 @@ setter = $setter; diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownUnitException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownUnitException.php index ecd7f7a59..833c4d73c 100644 --- a/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownUnitException.php +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownUnitException.php @@ -1,5 +1,7 @@ unit = $unit; diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/UnreachableException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnreachableException.php index 1654ab11b..c637d3b20 100644 --- a/vendor/nesbot/carbon/src/Carbon/Exceptions/UnreachableException.php +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnreachableException.php @@ -1,5 +1,7 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use Exception; + +/** + * @codeCoverageIgnore + */ +class UnsupportedUnitException extends UnitException +{ + public function __construct(string $unit, int $code = 0, ?Exception $previous = null) + { + parent::__construct("Unsupported unit '$unit'", $code, $previous); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Factory.php b/vendor/nesbot/carbon/src/Carbon/Factory.php index d497535f7..7831c158e 100644 --- a/vendor/nesbot/carbon/src/Carbon/Factory.php +++ b/vendor/nesbot/carbon/src/Carbon/Factory.php @@ -1,5 +1,7 @@ * - * @method bool canBeCreatedFromFormat($date, $format) Checks if the (date)time string is in a given format and valid to create a - * new instance. - * @method Carbon|false create($year = 0, $month = 1, $day = 1, $hour = 0, $minute = 0, $second = 0, $tz = null) Create a new Carbon instance from a specific date and time. - * If any of $year, $month or $day are set to null their now() values will - * be used. - * If $hour is null it will be set to its now() value and the default - * values for $minute and $second will be their now() values. - * If $hour is not null then the default values for $minute and $second - * will be 0. - * @method Carbon createFromDate($year = null, $month = null, $day = null, $tz = null) Create a Carbon instance from just a date. The time portion is set to now. - * @method Carbon|false createFromFormat($format, $time, $tz = null) Create a Carbon instance from a specific format. - * @method Carbon|false createFromIsoFormat($format, $time, $tz = null, $locale = 'en', $translator = null) Create a Carbon instance from a specific ISO format (same replacements as ->isoFormat()). - * @method Carbon|false createFromLocaleFormat($format, $locale, $time, $tz = null) Create a Carbon instance from a specific format and a string in a given language. - * @method Carbon|false createFromLocaleIsoFormat($format, $locale, $time, $tz = null) Create a Carbon instance from a specific ISO format and a string in a given language. - * @method Carbon createFromTime($hour = 0, $minute = 0, $second = 0, $tz = null) Create a Carbon instance from just a time. The date portion is set to today. - * @method Carbon createFromTimeString($time, $tz = null) Create a Carbon instance from a time string. The date portion is set to today. - * @method Carbon createFromTimestamp($timestamp, $tz = null) Create a Carbon instance from a timestamp and set the timezone (use default one if not specified). - * Timestamp input can be given as int, float or a string containing one or more numbers. - * @method Carbon createFromTimestampMs($timestamp, $tz = null) Create a Carbon instance from a timestamp in milliseconds. - * Timestamp input can be given as int, float or a string containing one or more numbers. - * @method Carbon createFromTimestampMsUTC($timestamp) Create a Carbon instance from a timestamp in milliseconds. - * Timestamp input can be given as int, float or a string containing one or more numbers. - * @method Carbon createFromTimestampUTC($timestamp) Create a Carbon instance from an timestamp keeping the timezone to UTC. - * Timestamp input can be given as int, float or a string containing one or more numbers. - * @method Carbon createMidnightDate($year = null, $month = null, $day = null, $tz = null) Create a Carbon instance from just a date. The time portion is set to midnight. - * @method Carbon|false createSafe($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $tz = null) Create a new safe Carbon instance from a specific date and time. - * If any of $year, $month or $day are set to null their now() values will - * be used. - * If $hour is null it will be set to its now() value and the default - * values for $minute and $second will be their now() values. - * If $hour is not null then the default values for $minute and $second - * will be 0. - * If one of the set values is not valid, an InvalidDateException - * will be thrown. - * @method CarbonInterface createStrict(?int $year = 0, ?int $month = 1, ?int $day = 1, ?int $hour = 0, ?int $minute = 0, ?int $second = 0, $tz = null) Create a new Carbon instance from a specific date and time using strict validation. - * @method Carbon disableHumanDiffOption($humanDiffOption) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use the ->settings() method. - * @method Carbon enableHumanDiffOption($humanDiffOption) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use the ->settings() method. - * @method mixed executeWithLocale($locale, $func) Set the current locale to the given, execute the passed function, reset the locale to previous one, - * then return the result of the closure (or null if the closure was void). - * @method Carbon fromSerialized($value) Create an instance from a serialized string. - * @method void genericMacro($macro, $priority = 0) Register a custom macro. - * @method array getAvailableLocales() Returns the list of internally available locales and already loaded custom locales. - * (It will ignore custom translator dynamic loading.) - * @method Language[] getAvailableLocalesInfo() Returns list of Language object for each available locale. This object allow you to get the ISO name, native - * name, region and variant of the locale. - * @method array getDays() Get the days of the week - * @method string|null getFallbackLocale() Get the fallback locale. - * @method array getFormatsToIsoReplacements() List of replacements from date() format to isoFormat(). - * @method int getHumanDiffOptions() Return default humanDiff() options (merged flags as integer). - * @method array getIsoUnits() Returns list of locale units for ISO formatting. - * @method array getLastErrors() {@inheritdoc} - * @method string getLocale() Get the current translator locale. - * @method callable|null getMacro($name) Get the raw callable macro registered globally for a given name. - * @method int getMidDayAt() get midday/noon hour - * @method Closure|Carbon getTestNow() Get the Carbon instance (real or mock) to be returned when a "now" - * instance is created. - * @method string getTimeFormatByPrecision($unitPrecision) Return a format from H:i to H:i:s.u according to given unit precision. - * @method string getTranslationMessageWith($translator, string $key, ?string $locale = null, ?string $default = null) Returns raw translation message for a given key. - * @method \Symfony\Component\Translation\TranslatorInterface getTranslator() Get the default translator instance in use. - * @method int getWeekEndsAt() Get the last day of week - * @method int getWeekStartsAt() Get the first day of week - * @method array getWeekendDays() Get weekend days - * @method bool hasFormat($date, $format) Checks if the (date)time string is in a given format. - * @method bool hasFormatWithModifiers($date, $format) Checks if the (date)time string is in a given format. - * @method bool hasMacro($name) Checks if macro is registered globally. - * @method bool hasRelativeKeywords($time) Determine if a time string will produce a relative date. - * @method bool hasTestNow() Determine if there is a valid test instance set. A valid test instance - * is anything that is not null. - * @method Carbon instance($date) Create a Carbon instance from a DateTime one. - * @method bool isImmutable() Returns true if the current class/instance is immutable. - * @method bool isModifiableUnit($unit) Returns true if a property can be changed via setter. - * @method bool isMutable() Returns true if the current class/instance is mutable. - * @method bool isStrictModeEnabled() Returns true if the strict mode is globally in use, false else. - * (It can be overridden in specific instances.) - * @method bool localeHasDiffOneDayWords($locale) Returns true if the given locale is internally supported and has words for 1-day diff (just now, yesterday, tomorrow). - * Support is considered enabled if the 3 words are translated in the given locale. - * @method bool localeHasDiffSyntax($locale) Returns true if the given locale is internally supported and has diff syntax support (ago, from now, before, after). - * Support is considered enabled if the 4 sentences are translated in the given locale. - * @method bool localeHasDiffTwoDayWords($locale) Returns true if the given locale is internally supported and has words for 2-days diff (before yesterday, after tomorrow). - * Support is considered enabled if the 2 words are translated in the given locale. - * @method bool localeHasPeriodSyntax($locale) Returns true if the given locale is internally supported and has period syntax support (X times, every X, from X, to X). - * Support is considered enabled if the 4 sentences are translated in the given locale. - * @method bool localeHasShortUnits($locale) Returns true if the given locale is internally supported and has short-units support. - * Support is considered enabled if either year, day or hour has a short variant translated. - * @method void macro($name, $macro) Register a custom macro. - * @method Carbon|null make($var) Make a Carbon instance from given variable if possible. - * Always return a new instance. Parse only strings and only these likely to be dates (skip intervals - * and recurrences). Throw an exception for invalid format, but otherwise return null. - * @method Carbon maxValue() Create a Carbon instance for the greatest supported date. - * @method Carbon minValue() Create a Carbon instance for the lowest supported date. - * @method void mixin($mixin) Mix another object into the class. - * @method Carbon now($tz = null) Get a Carbon instance for the current date and time. - * @method Carbon parse($time = null, $tz = null) Create a carbon instance from a string. - * This is an alias for the constructor that allows better fluent syntax - * as it allows you to do Carbon::parse('Monday next week')->fn() rather - * than (new Carbon('Monday next week'))->fn(). - * @method Carbon parseFromLocale($time, $locale = null, $tz = null) Create a carbon instance from a localized string (in French, Japanese, Arabic, etc.). - * @method string pluralUnit(string $unit) Returns standardized plural of a given singular/plural unit name (in English). - * @method Carbon|false rawCreateFromFormat($format, $time, $tz = null) Create a Carbon instance from a specific format. - * @method Carbon rawParse($time = null, $tz = null) Create a carbon instance from a string. - * This is an alias for the constructor that allows better fluent syntax - * as it allows you to do Carbon::parse('Monday next week')->fn() rather - * than (new Carbon('Monday next week'))->fn(). - * @method Carbon resetMacros() Remove all macros and generic macros. - * @method void resetMonthsOverflow() @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use the ->settings() method. - * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants - * are available for quarters, years, decade, centuries, millennia (singular and plural forms). - * @method void resetToStringFormat() Reset the format used to the default when type juggling a Carbon instance to a string - * @method void resetYearsOverflow() @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use the ->settings() method. - * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants - * are available for quarters, years, decade, centuries, millennia (singular and plural forms). - * @method void serializeUsing($callback) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather transform Carbon object before the serialization. - * JSON serialize all Carbon instances using the given callback. - * @method Carbon setFallbackLocale($locale) Set the fallback locale. - * @method Carbon setHumanDiffOptions($humanDiffOptions) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use the ->settings() method. - * @method bool setLocale($locale) Set the current translator locale and indicate if the source locale file exists. - * Pass 'auto' as locale to use closest language from the current LC_TIME locale. - * @method void setMidDayAt($hour) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather consider mid-day is always 12pm, then if you need to test if it's an other - * hour, test it explicitly: - * $date->format('G') == 13 - * or to set explicitly to a given hour: - * $date->setTime(13, 0, 0, 0) - * Set midday/noon hour - * @method Carbon setTestNow($testNow = null) Set a Carbon instance (real or mock) to be returned when a "now" - * instance is created. The provided instance will be returned - * specifically under the following conditions: - * - A call to the static now() method, ex. Carbon::now() - * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null) - * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now') - * - When a string containing the desired time is passed to Carbon::parse(). - * Note the timezone parameter was left out of the examples above and - * has no affect as the mock value will be returned regardless of its value. - * Only the moment is mocked with setTestNow(), the timezone will still be the one passed - * as parameter of date_default_timezone_get() as a fallback (see setTestNowAndTimezone()). - * To clear the test instance call this method using the default - * parameter of null. - * /!\ Use this method for unit tests only. - * @method Carbon setTestNowAndTimezone($testNow = null, $tz = null) Set a Carbon instance (real or mock) to be returned when a "now" - * instance is created. The provided instance will be returned - * specifically under the following conditions: - * - A call to the static now() method, ex. Carbon::now() - * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null) - * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now') - * - When a string containing the desired time is passed to Carbon::parse(). - * It will also align default timezone (e.g. call date_default_timezone_set()) with - * the second argument or if null, with the timezone of the given date object. - * To clear the test instance call this method using the default - * parameter of null. - * /!\ Use this method for unit tests only. - * @method void setToStringFormat($format) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather let Carbon object being cast to string with DEFAULT_TO_STRING_FORMAT, and - * use other method or custom format passed to format() method if you need to dump another string - * format. - * Set the default format used when type juggling a Carbon instance to a string. - * @method void setTranslator(TranslatorInterface $translator) Set the default translator instance to use. - * @method Carbon setUtf8($utf8) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use UTF-8 language packages on every machine. - * Set if UTF8 will be used for localized date/time. - * @method void setWeekEndsAt($day) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * Use $weekStartsAt optional parameter instead when using startOfWeek, floorWeek, ceilWeek - * or roundWeek method. You can also use the 'first_day_of_week' locale setting to change the - * start of week according to current locale selected and implicitly the end of week. - * Set the last day of week - * @method void setWeekStartsAt($day) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * Use $weekEndsAt optional parameter instead when using endOfWeek method. You can also use the - * 'first_day_of_week' locale setting to change the start of week according to current locale - * selected and implicitly the end of week. - * Set the first day of week - * @method void setWeekendDays($days) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather consider week-end is always saturday and sunday, and if you have some custom - * week-end days to handle, give to those days an other name and create a macro for them: - * ``` - * Carbon::macro('isDayOff', function ($date) { - * return $date->isSunday() || $date->isMonday(); - * }); - * Carbon::macro('isNotDayOff', function ($date) { - * return !$date->isDayOff(); - * }); - * if ($someDate->isDayOff()) ... - * if ($someDate->isNotDayOff()) ... - * // Add 5 not-off days - * $count = 5; - * while ($someDate->isDayOff() || ($count-- > 0)) { - * $someDate->addDay(); - * } - * ``` - * Set weekend days - * @method bool shouldOverflowMonths() Get the month overflow global behavior (can be overridden in specific instances). - * @method bool shouldOverflowYears() Get the month overflow global behavior (can be overridden in specific instances). - * @method string singularUnit(string $unit) Returns standardized singular of a given singular/plural unit name (in English). - * @method Carbon today($tz = null) Create a Carbon instance for today. - * @method Carbon tomorrow($tz = null) Create a Carbon instance for tomorrow. - * @method string translateTimeString($timeString, $from = null, $to = null, $mode = CarbonInterface::TRANSLATE_ALL) Translate a time string from a locale to an other. - * @method string translateWith(TranslatorInterface $translator, string $key, array $parameters = [], $number = null) Translate using translation string or callback available. - * @method void useMonthsOverflow($monthsOverflow = true) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use the ->settings() method. - * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants - * are available for quarters, years, decade, centuries, millennia (singular and plural forms). - * @method Carbon useStrictMode($strictModeEnabled = true) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use the ->settings() method. - * @method void useYearsOverflow($yearsOverflow = true) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use the ->settings() method. - * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants - * are available for quarters, years, decade, centuries, millennia (singular and plural forms). - * @method mixed withTestNow($testNow, $callback) Temporarily sets a static date to be used within the callback. - * Using setTestNow to set the date, executing the callback, then - * clearing the test instance. - * /!\ Use this method for unit tests only. - * @method Carbon yesterday($tz = null) Create a Carbon instance for yesterday. + * @method bool canBeCreatedFromFormat(?string $date, string $format) Checks if the (date)time string is in a given format and valid to create a + * new instance. + * @method ?Carbon create($year = 0, $month = 1, $day = 1, $hour = 0, $minute = 0, $second = 0, $timezone = null) Create a new Carbon instance from a specific date and time. + * If any of $year, $month or $day are set to null their now() values will + * be used. + * If $hour is null it will be set to its now() value and the default + * values for $minute and $second will be their now() values. + * If $hour is not null then the default values for $minute and $second + * will be 0. + * @method Carbon createFromDate($year = null, $month = null, $day = null, $timezone = null) Create a Carbon instance from just a date. The time portion is set to now. + * @method ?Carbon createFromFormat($format, $time, $timezone = null) Create a Carbon instance from a specific format. + * @method ?Carbon createFromIsoFormat(string $format, string $time, $timezone = null, ?string $locale = 'en', ?TranslatorInterface $translator = null) Create a Carbon instance from a specific ISO format (same replacements as ->isoFormat()). + * @method ?Carbon createFromLocaleFormat(string $format, string $locale, string $time, $timezone = null) Create a Carbon instance from a specific format and a string in a given language. + * @method ?Carbon createFromLocaleIsoFormat(string $format, string $locale, string $time, $timezone = null) Create a Carbon instance from a specific ISO format and a string in a given language. + * @method Carbon createFromTime($hour = 0, $minute = 0, $second = 0, $timezone = null) Create a Carbon instance from just a time. The date portion is set to today. + * @method Carbon createFromTimeString(string $time, DateTimeZone|string|int|null $timezone = null) Create a Carbon instance from a time string. The date portion is set to today. + * @method Carbon createFromTimestamp(string|int|float $timestamp, DateTimeZone|string|int|null $timezone = null) Create a Carbon instance from a timestamp and set the timezone (UTC by default). + * Timestamp input can be given as int, float or a string containing one or more numbers. + * @method Carbon createFromTimestampMs(string|int|float $timestamp, DateTimeZone|string|int|null $timezone = null) Create a Carbon instance from a timestamp in milliseconds. + * Timestamp input can be given as int, float or a string containing one or more numbers. + * @method Carbon createFromTimestampMsUTC($timestamp) Create a Carbon instance from a timestamp in milliseconds. + * Timestamp input can be given as int, float or a string containing one or more numbers. + * @method Carbon createFromTimestampUTC(string|int|float $timestamp) Create a Carbon instance from a timestamp keeping the timezone to UTC. + * Timestamp input can be given as int, float or a string containing one or more numbers. + * @method Carbon createMidnightDate($year = null, $month = null, $day = null, $timezone = null) Create a Carbon instance from just a date. The time portion is set to midnight. + * @method ?Carbon createSafe($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $timezone = null) Create a new safe Carbon instance from a specific date and time. + * If any of $year, $month or $day are set to null their now() values will + * be used. + * If $hour is null it will be set to its now() value and the default + * values for $minute and $second will be their now() values. + * If $hour is not null then the default values for $minute and $second + * will be 0. + * If one of the set values is not valid, an InvalidDateException + * will be thrown. + * @method Carbon createStrict(?int $year = 0, ?int $month = 1, ?int $day = 1, ?int $hour = 0, ?int $minute = 0, ?int $second = 0, $timezone = null) Create a new Carbon instance from a specific date and time using strict validation. + * @method mixed executeWithLocale(string $locale, callable $func) Set the current locale to the given, execute the passed function, reset the locale to previous one, + * then return the result of the closure (or null if the closure was void). + * @method Carbon fromSerialized($value) Create an instance from a serialized string. + * @method array getAvailableLocales() Returns the list of internally available locales and already loaded custom locales. + * (It will ignore custom translator dynamic loading.) + * @method Language[] getAvailableLocalesInfo() Returns list of Language object for each available locale. This object allow you to get the ISO name, native + * name, region and variant of the locale. + * @method array getDays() Get the days of the week. + * @method ?string getFallbackLocale() Get the fallback locale. + * @method array getFormatsToIsoReplacements() List of replacements from date() format to isoFormat(). + * @method array getIsoUnits() Returns list of locale units for ISO formatting. + * @method array|false getLastErrors() {@inheritdoc} + * @method string getLocale() Get the current translator locale. + * @method int getMidDayAt() get midday/noon hour + * @method string getTimeFormatByPrecision(string $unitPrecision) Return a format from H:i to H:i:s.u according to given unit precision. + * @method string|Closure|null getTranslationMessageWith($translator, string $key, ?string $locale = null, ?string $default = null) Returns raw translation message for a given key. + * @method int getWeekEndsAt(?string $locale = null) Get the last day of week. + * @method int getWeekStartsAt(?string $locale = null) Get the first day of week. + * @method bool hasRelativeKeywords(?string $time) Determine if a time string will produce a relative date. + * @method Carbon instance(DateTimeInterface $date) Create a Carbon instance from a DateTime one. + * @method bool isImmutable() Returns true if the current class/instance is immutable. + * @method bool isModifiableUnit($unit) Returns true if a property can be changed via setter. + * @method bool isMutable() Returns true if the current class/instance is mutable. + * @method bool localeHasDiffOneDayWords(string $locale) Returns true if the given locale is internally supported and has words for 1-day diff (just now, yesterday, tomorrow). + * Support is considered enabled if the 3 words are translated in the given locale. + * @method bool localeHasDiffSyntax(string $locale) Returns true if the given locale is internally supported and has diff syntax support (ago, from now, before, after). + * Support is considered enabled if the 4 sentences are translated in the given locale. + * @method bool localeHasDiffTwoDayWords(string $locale) Returns true if the given locale is internally supported and has words for 2-days diff (before yesterday, after tomorrow). + * Support is considered enabled if the 2 words are translated in the given locale. + * @method bool localeHasPeriodSyntax($locale) Returns true if the given locale is internally supported and has period syntax support (X times, every X, from X, to X). + * Support is considered enabled if the 4 sentences are translated in the given locale. + * @method bool localeHasShortUnits(string $locale) Returns true if the given locale is internally supported and has short-units support. + * Support is considered enabled if either year, day or hour has a short variant translated. + * @method ?Carbon make($var, DateTimeZone|string|null $timezone = null) Make a Carbon instance from given variable if possible. + * Always return a new instance. Parse only strings and only these likely to be dates (skip intervals + * and recurrences). Throw an exception for invalid format, but otherwise return null. + * @method void mixin(object|string $mixin) Mix another object into the class. + * @method Carbon now(DateTimeZone|string|int|null $timezone = null) Get a Carbon instance for the current date and time. + * @method Carbon parse(DateTimeInterface|WeekDay|Month|string|int|float|null $time, DateTimeZone|string|int|null $timezone = null) Create a carbon instance from a string. + * This is an alias for the constructor that allows better fluent syntax + * as it allows you to do Carbon::parse('Monday next week')->fn() rather + * than (new Carbon('Monday next week'))->fn(). + * @method Carbon parseFromLocale(string $time, ?string $locale = null, DateTimeZone|string|int|null $timezone = null) Create a carbon instance from a localized string (in French, Japanese, Arabic, etc.). + * @method string pluralUnit(string $unit) Returns standardized plural of a given singular/plural unit name (in English). + * @method ?Carbon rawCreateFromFormat(string $format, string $time, $timezone = null) Create a Carbon instance from a specific format. + * @method Carbon rawParse(DateTimeInterface|WeekDay|Month|string|int|float|null $time, DateTimeZone|string|int|null $timezone = null) Create a carbon instance from a string. + * This is an alias for the constructor that allows better fluent syntax + * as it allows you to do Carbon::parse('Monday next week')->fn() rather + * than (new Carbon('Monday next week'))->fn(). + * @method void setFallbackLocale(string $locale) Set the fallback locale. + * @method void setLocale(string $locale) Set the current translator locale and indicate if the source locale file exists. + * Pass 'auto' as locale to use the closest language to the current LC_TIME locale. + * @method void setMidDayAt($hour) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather consider mid-day is always 12pm, then if you need to test if it's an other + * hour, test it explicitly: + * $date->format('G') == 13 + * or to set explicitly to a given hour: + * $date->setTime(13, 0, 0, 0) + * Set midday/noon hour + * @method string singularUnit(string $unit) Returns standardized singular of a given singular/plural unit name (in English). + * @method void sleep(int|float $seconds) + * @method Carbon today(DateTimeZone|string|int|null $timezone = null) Create a Carbon instance for today. + * @method Carbon tomorrow(DateTimeZone|string|int|null $timezone = null) Create a Carbon instance for tomorrow. + * @method string translateTimeString(string $timeString, ?string $from = null, ?string $to = null, int $mode = CarbonInterface::TRANSLATE_ALL) Translate a time string from a locale to an other. + * @method string translateWith(TranslatorInterface $translator, string $key, array $parameters = [], $number = null) Translate using translation string or callback available. + * @method Carbon yesterday(DateTimeZone|string|int|null $timezone = null) Create a Carbon instance for yesterday. * * */ class Factory { - protected $className = Carbon::class; + protected string $className = Carbon::class; + + protected array $settings = []; + + /** + * A test Carbon instance to be returned when now instances are created. + */ + protected Closure|CarbonInterface|null $testNow = null; + + /** + * The timezone to restore to when clearing the time mock. + */ + protected ?string $testDefaultTimezone = null; + + /** + * Is true when test-now is generated by a closure and timezone should be taken on the fly from it. + */ + protected bool $useTimezoneFromTestNow = false; + + /** + * Default translator. + */ + protected TranslatorInterface $translator; - protected $settings = []; + /** + * Days of weekend. + */ + protected array $weekendDays = [ + CarbonInterface::SATURDAY, + CarbonInterface::SUNDAY, + ]; + + /** + * Format regex patterns. + * + * @var array + */ + protected array $regexFormats = [ + 'd' => '(3[01]|[12][0-9]|0[1-9])', + 'D' => '(Sun|Mon|Tue|Wed|Thu|Fri|Sat)', + 'j' => '([123][0-9]|[1-9])', + 'l' => '([a-zA-Z]{2,})', + 'N' => '([1-7])', + 'S' => '(st|nd|rd|th)', + 'w' => '([0-6])', + 'z' => '(36[0-5]|3[0-5][0-9]|[12][0-9]{2}|[1-9]?[0-9])', + 'W' => '(5[012]|[1-4][0-9]|0?[1-9])', + 'F' => '([a-zA-Z]{2,})', + 'm' => '(1[012]|0[1-9])', + 'M' => '([a-zA-Z]{3})', + 'n' => '(1[012]|[1-9])', + 't' => '(2[89]|3[01])', + 'L' => '(0|1)', + 'o' => '([1-9][0-9]{0,4})', + 'Y' => '([1-9]?[0-9]{4})', + 'y' => '([0-9]{2})', + 'a' => '(am|pm)', + 'A' => '(AM|PM)', + 'B' => '([0-9]{3})', + 'g' => '(1[012]|[1-9])', + 'G' => '(2[0-3]|1?[0-9])', + 'h' => '(1[012]|0[1-9])', + 'H' => '(2[0-3]|[01][0-9])', + 'i' => '([0-5][0-9])', + 's' => '([0-5][0-9])', + 'u' => '([0-9]{1,6})', + 'v' => '([0-9]{1,3})', + 'e' => '([a-zA-Z]{1,5})|([a-zA-Z]*\\/[a-zA-Z]*)', + 'I' => '(0|1)', + 'O' => '([+-](1[0123]|0[0-9])[0134][05])', + 'P' => '([+-](1[0123]|0[0-9]):[0134][05])', + 'p' => '(Z|[+-](1[0123]|0[0-9]):[0134][05])', + 'T' => '([a-zA-Z]{1,5})', + 'Z' => '(-?[1-5]?[0-9]{1,4})', + 'U' => '([0-9]*)', + + // The formats below are combinations of the above formats. + 'c' => '(([1-9]?[0-9]{4})-(1[012]|0[1-9])-(3[01]|[12][0-9]|0[1-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])[+-](1[012]|0[0-9]):([0134][05]))', // Y-m-dTH:i:sP + 'r' => '(([a-zA-Z]{3}), ([123][0-9]|0[1-9]) ([a-zA-Z]{3}) ([1-9]?[0-9]{4}) (2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]) [+-](1[012]|0[0-9])([0134][05]))', // D, d M Y H:i:s O + ]; + + /** + * Format modifiers (such as available in createFromFormat) regex patterns. + * + * @var array + */ + protected array $regexFormatModifiers = [ + '*' => '.+', + ' ' => '[ ]', + '#' => '[;:\\/.,()-]', + '?' => '([^a]|[a])', + '!' => '', + '|' => '', + '+' => '', + ]; public function __construct(array $settings = [], ?string $className = null) { @@ -254,56 +239,497 @@ public function __construct(array $settings = [], ?string $className = null) $this->settings = $settings; } - public function getClassName() + public function getClassName(): string { return $this->className; } - public function setClassName(string $className) + public function setClassName(string $className): self { $this->className = $className; return $this; } - public function className(string $className = null) + public function className(?string $className = null): self|string { return $className === null ? $this->getClassName() : $this->setClassName($className); } - public function getSettings() + public function getSettings(): array { return $this->settings; } - public function setSettings(array $settings) + public function setSettings(array $settings): self { $this->settings = $settings; return $this; } - public function settings(array $settings = null) + public function settings(?array $settings = null): self|array { return $settings === null ? $this->getSettings() : $this->setSettings($settings); } - public function mergeSettings(array $settings) + public function mergeSettings(array $settings): self { $this->settings = array_merge($this->settings, $settings); return $this; } - public function __call($name, $arguments) + public function setHumanDiffOptions(int $humanDiffOptions): void + { + $this->mergeSettings([ + 'humanDiffOptions' => $humanDiffOptions, + ]); + } + + public function enableHumanDiffOption($humanDiffOption): void + { + $this->setHumanDiffOptions($this->getHumanDiffOptions() | $humanDiffOption); + } + + public function disableHumanDiffOption(int $humanDiffOption): void + { + $this->setHumanDiffOptions($this->getHumanDiffOptions() & ~$humanDiffOption); + } + + public function getHumanDiffOptions(): int + { + return (int) ($this->getSettings()['humanDiffOptions'] ?? 0); + } + + /** + * Register a custom macro. + * + * Pass null macro to remove it. + * + * @example + * ``` + * $userSettings = [ + * 'locale' => 'pt', + * 'timezone' => 'America/Sao_Paulo', + * ]; + * $factory->macro('userFormat', function () use ($userSettings) { + * return $this->copy()->locale($userSettings['locale'])->tz($userSettings['timezone'])->calendar(); + * }); + * echo $factory->yesterday()->hours(11)->userFormat(); + * ``` + * + * @param-closure-this static $macro + */ + public function macro(string $name, ?callable $macro): void + { + $macros = $this->getSettings()['macros'] ?? []; + $macros[$name] = $macro; + + $this->mergeSettings([ + 'macros' => $macros, + ]); + } + + /** + * Remove all macros and generic macros. + */ + public function resetMacros(): void + { + $this->mergeSettings([ + 'macros' => null, + 'genericMacros' => null, + ]); + } + + /** + * Register a custom macro. + * + * @param callable $macro + * @param int $priority marco with higher priority is tried first + * + * @return void + */ + public function genericMacro(callable $macro, int $priority = 0): void + { + $genericMacros = $this->getSettings()['genericMacros'] ?? []; + + if (!isset($genericMacros[$priority])) { + $genericMacros[$priority] = []; + krsort($genericMacros, SORT_NUMERIC); + } + + $genericMacros[$priority][] = $macro; + + $this->mergeSettings([ + 'genericMacros' => $genericMacros, + ]); + } + + /** + * Checks if macro is registered globally. + */ + public function hasMacro(string $name): bool + { + return isset($this->getSettings()['macros'][$name]); + } + + /** + * Get the raw callable macro registered globally for a given name. + */ + public function getMacro(string $name): ?callable + { + return $this->getSettings()['macros'][$name] ?? null; + } + + /** + * Set the default translator instance to use. + */ + public function setTranslator(TranslatorInterface $translator): void + { + $this->translator = $translator; + } + + /** + * Initialize the default translator instance if necessary. + */ + public function getTranslator(): TranslatorInterface + { + return $this->translator ??= Translator::get(); + } + + /** + * Reset the format used to the default when type juggling a Carbon instance to a string + * + * @return void + */ + public function resetToStringFormat(): void + { + $this->setToStringFormat(null); + } + + /** + * Set the default format used when type juggling a Carbon instance to a string. + */ + public function setToStringFormat(string|Closure|null $format): void + { + $this->mergeSettings([ + 'toStringFormat' => $format, + ]); + } + + /** + * JSON serialize all Carbon instances using the given callback. + */ + public function serializeUsing(string|callable|null $format): void + { + $this->mergeSettings([ + 'toJsonFormat' => $format, + ]); + } + + /** + * Enable the strict mode (or disable with passing false). + */ + public function useStrictMode(bool $strictModeEnabled = true): void + { + $this->mergeSettings([ + 'strictMode' => $strictModeEnabled, + ]); + } + + /** + * Returns true if the strict mode is globally in use, false else. + * (It can be overridden in specific instances.) + */ + public function isStrictModeEnabled(): bool + { + return $this->getSettings()['strictMode'] ?? true; + } + + /** + * Indicates if months should be calculated with overflow. + */ + public function useMonthsOverflow(bool $monthsOverflow = true): void + { + $this->mergeSettings([ + 'monthOverflow' => $monthsOverflow, + ]); + } + + /** + * Reset the month overflow behavior. + */ + public function resetMonthsOverflow(): void + { + $this->useMonthsOverflow(); + } + + /** + * Get the month overflow global behavior (can be overridden in specific instances). + */ + public function shouldOverflowMonths(): bool + { + return $this->getSettings()['monthOverflow'] ?? true; + } + + /** + * Indicates if years should be calculated with overflow. + */ + public function useYearsOverflow(bool $yearsOverflow = true): void + { + $this->mergeSettings([ + 'yearOverflow' => $yearsOverflow, + ]); + } + + /** + * Reset the month overflow behavior. + */ + public function resetYearsOverflow(): void + { + $this->useYearsOverflow(); + } + + /** + * Get the month overflow global behavior (can be overridden in specific instances). + */ + public function shouldOverflowYears(): bool + { + return $this->getSettings()['yearOverflow'] ?? true; + } + + /** + * Get weekend days + * + * @return array + */ + public function getWeekendDays(): array + { + return $this->weekendDays; + } + + /** + * Set weekend days + */ + public function setWeekendDays(array $days): void + { + $this->weekendDays = $days; + } + + /** + * Checks if the (date)time string is in a given format. + * + * @example + * ``` + * Carbon::hasFormat('11:12:45', 'h:i:s'); // true + * Carbon::hasFormat('13:12:45', 'h:i:s'); // false + * ``` + */ + public function hasFormat(string $date, string $format): bool + { + // createFromFormat() is known to handle edge cases silently. + // E.g. "1975-5-1" (Y-n-j) will still be parsed correctly when "Y-m-d" is supplied as the format. + // To ensure we're really testing against our desired format, perform an additional regex validation. + + return $this->matchFormatPattern($date, preg_quote($format, '/'), $this->regexFormats); + } + + /** + * Checks if the (date)time string is in a given format. + * + * @example + * ``` + * Carbon::hasFormatWithModifiers('31/08/2015', 'd#m#Y'); // true + * Carbon::hasFormatWithModifiers('31/08/2015', 'm#d#Y'); // false + * ``` + */ + public function hasFormatWithModifiers(string $date, string $format): bool + { + return $this->matchFormatPattern($date, $format, array_merge($this->regexFormats, $this->regexFormatModifiers)); + } + + /** + * Set a Carbon instance (real or mock) to be returned when a "now" + * instance is created. The provided instance will be returned + * specifically under the following conditions: + * - A call to the static now() method, ex. Carbon::now() + * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null) + * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now') + * - When a string containing the desired time is passed to Carbon::parse(). + * + * Note the timezone parameter was left out of the examples above and + * has no affect as the mock value will be returned regardless of its value. + * + * Only the moment is mocked with setTestNow(), the timezone will still be the one passed + * as parameter of date_default_timezone_get() as a fallback (see setTestNowAndTimezone()). + * + * To clear the test instance call this method using the default + * parameter of null. + * + * /!\ Use this method for unit tests only. + * + * @param DateTimeInterface|Closure|static|string|false|null $testNow real or mock Carbon instance + */ + public function setTestNow(mixed $testNow = null): void + { + $this->useTimezoneFromTestNow = false; + $this->testNow = $testNow instanceof self || $testNow instanceof Closure + ? $testNow + : $this->make($testNow); + } + + /** + * Set a Carbon instance (real or mock) to be returned when a "now" + * instance is created. The provided instance will be returned + * specifically under the following conditions: + * - A call to the static now() method, ex. Carbon::now() + * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null) + * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now') + * - When a string containing the desired time is passed to Carbon::parse(). + * + * It will also align default timezone (e.g. call date_default_timezone_set()) with + * the second argument or if null, with the timezone of the given date object. + * + * To clear the test instance call this method using the default + * parameter of null. + * + * /!\ Use this method for unit tests only. + * + * @param DateTimeInterface|Closure|static|string|false|null $testNow real or mock Carbon instance + */ + public function setTestNowAndTimezone(mixed $testNow = null, $timezone = null): void + { + if ($testNow) { + $this->testDefaultTimezone ??= date_default_timezone_get(); + } + + $useDateInstanceTimezone = $testNow instanceof DateTimeInterface; + + if ($useDateInstanceTimezone) { + $this->setDefaultTimezone($testNow->getTimezone()->getName(), $testNow); + } + + $this->setTestNow($testNow); + $this->useTimezoneFromTestNow = ($timezone === null && $testNow instanceof Closure); + + if (!$useDateInstanceTimezone) { + $now = $this->getMockedTestNow(\func_num_args() === 1 ? null : $timezone); + $this->setDefaultTimezone($now?->tzName ?? $this->testDefaultTimezone ?? 'UTC', $now); + } + + if (!$testNow) { + $this->testDefaultTimezone = null; + } + } + + /** + * Temporarily sets a static date to be used within the callback. + * Using setTestNow to set the date, executing the callback, then + * clearing the test instance. + * + * /!\ Use this method for unit tests only. + * + * @template T + * + * @param DateTimeInterface|Closure|static|string|false|null $testNow real or mock Carbon instance + * @param Closure(): T $callback + * + * @return T + */ + public function withTestNow(mixed $testNow, callable $callback): mixed + { + $this->setTestNow($testNow); + + try { + $result = $callback(); + } finally { + $this->setTestNow(); + } + + return $result; + } + + /** + * Get the Carbon instance (real or mock) to be returned when a "now" + * instance is created. + * + * @return Closure|CarbonInterface|null the current instance used for testing + */ + public function getTestNow(): Closure|CarbonInterface|null + { + if ($this->testNow === null) { + $factory = FactoryImmutable::getDefaultInstance(); + + if ($factory !== $this) { + return $factory->getTestNow(); + } + } + + return $this->testNow; + } + + public function handleTestNowClosure( + Closure|CarbonInterface|null $testNow, + DateTimeZone|string|int|null $timezone = null, + ): ?CarbonInterface { + if ($testNow instanceof Closure) { + $callback = Callback::fromClosure($testNow); + $realNow = new DateTimeImmutable('now'); + $testNow = $testNow($callback->prepareParameter($this->parse( + $realNow->format('Y-m-d H:i:s.u'), + $timezone ?? $realNow->getTimezone(), + ))); + + if ($testNow !== null && !($testNow instanceof DateTimeInterface)) { + $function = $callback->getReflectionFunction(); + $type = \is_object($testNow) ? $testNow::class : \gettype($testNow); + + throw new RuntimeException( + 'The test closure defined in '.$function->getFileName(). + ' at line '.$function->getStartLine().' returned '.$type. + '; expected '.CarbonInterface::class.'|null', + ); + } + + if (!($testNow instanceof CarbonInterface)) { + $timezone ??= $this->useTimezoneFromTestNow ? $testNow->getTimezone() : null; + $testNow = $this->__call('instance', [$testNow, $timezone]); + } + } + + return $testNow; + } + + /** + * Determine if there is a valid test instance set. A valid test instance + * is anything that is not null. + * + * @return bool true if there is a test instance, otherwise false + */ + public function hasTestNow(): bool + { + return $this->getTestNow() !== null; + } + + public function withTimeZone(DateTimeZone|string|int|null $timezone): static + { + $factory = clone $this; + $factory->settings['timezone'] = $timezone; + + return $factory; + } + + public function __call(string $name, array $arguments): mixed { $method = new ReflectionMethod($this->className, $name); $settings = $this->settings; if ($settings && isset($settings['timezone'])) { - $tzParameters = array_filter($method->getParameters(), function ($parameter) { + $timezoneParameters = array_filter($method->getParameters(), function ($parameter) { return \in_array($parameter->getName(), ['tz', 'timezone'], true); }); + $timezoneSetting = $settings['timezone']; if (isset($arguments[0]) && \in_array($name, ['instance', 'make', 'create', 'parse'], true)) { if ($arguments[0] instanceof DateTimeInterface) { @@ -311,16 +737,112 @@ public function __call($name, $arguments) } elseif (\is_string($arguments[0]) && date_parse($arguments[0])['is_localtime']) { unset($settings['timezone'], $settings['innerTimezone']); } - } elseif (\count($tzParameters)) { - array_splice($arguments, key($tzParameters), 0, [$settings['timezone']]); + } + + if (\count($timezoneParameters)) { + $index = key($timezoneParameters); + + if (!isset($arguments[$index])) { + array_splice($arguments, key($timezoneParameters), 0, [$timezoneSetting]); + } + unset($settings['timezone']); } } - $result = $this->className::$name(...$arguments); + $clock = FactoryImmutable::getCurrentClock(); + FactoryImmutable::setCurrentClock($this); + + try { + $result = $this->className::$name(...$arguments); + } finally { + FactoryImmutable::setCurrentClock($clock); + } + + if (isset($this->translator)) { + $settings['translator'] = $this->translator; + } return $result instanceof CarbonInterface && !empty($settings) ? $result->settings($settings) : $result; } + + /** + * Get the mocked date passed in setTestNow() and if it's a Closure, execute it. + */ + protected function getMockedTestNow(DateTimeZone|string|int|null $timezone): ?CarbonInterface + { + $testNow = $this->handleTestNowClosure($this->getTestNow()); + + if ($testNow instanceof CarbonInterface) { + $testNow = $testNow->avoidMutation(); + + if ($timezone !== null) { + return $testNow->setTimezone($timezone); + } + } + + return $testNow; + } + + /** + * Checks if the (date)time string is in a given format with + * given list of pattern replacements. + * + * @example + * ``` + * Carbon::hasFormat('11:12:45', 'h:i:s'); // true + * Carbon::hasFormat('13:12:45', 'h:i:s'); // false + * ``` + * + * @param string $date + * @param string $format + * @param array $replacements + * + * @return bool + */ + private function matchFormatPattern(string $date, string $format, array $replacements): bool + { + // Preg quote, but remove escaped backslashes since we'll deal with escaped characters in the format string. + $regex = str_replace('\\\\', '\\', $format); + // Replace not-escaped letters + $regex = preg_replace_callback( + '/(? $match[1].strtr($match[2], $replacements), + $regex, + ); + // Replace escaped letters by the letter itself + $regex = preg_replace('/(?toRegionName($date); + + throw new InvalidArgumentException( + "Timezone ID '$timezone' is invalid". + ($suggestion && $suggestion !== $timezone ? ", did you mean '$suggestion'?" : '.')."\n". + "It must be one of the IDs from DateTimeZone::listIdentifiers(),\n". + 'For the record, hours/minutes offset are relevant only for a particular moment, '. + 'but not as a default timezone.', + 0, + $previous + ); + } + } } diff --git a/vendor/nesbot/carbon/src/Carbon/FactoryImmutable.php b/vendor/nesbot/carbon/src/Carbon/FactoryImmutable.php index d88a1cf67..e1d6f03ef 100644 --- a/vendor/nesbot/carbon/src/Carbon/FactoryImmutable.php +++ b/vendor/nesbot/carbon/src/Carbon/FactoryImmutable.php @@ -1,5 +1,7 @@ * - * @method bool canBeCreatedFromFormat($date, $format) Checks if the (date)time string is in a given format and valid to create a - * new instance. - * @method CarbonImmutable|false create($year = 0, $month = 1, $day = 1, $hour = 0, $minute = 0, $second = 0, $tz = null) Create a new Carbon instance from a specific date and time. - * If any of $year, $month or $day are set to null their now() values will - * be used. - * If $hour is null it will be set to its now() value and the default - * values for $minute and $second will be their now() values. - * If $hour is not null then the default values for $minute and $second - * will be 0. - * @method CarbonImmutable createFromDate($year = null, $month = null, $day = null, $tz = null) Create a Carbon instance from just a date. The time portion is set to now. - * @method CarbonImmutable|false createFromFormat($format, $time, $tz = null) Create a Carbon instance from a specific format. - * @method CarbonImmutable|false createFromIsoFormat($format, $time, $tz = null, $locale = 'en', $translator = null) Create a Carbon instance from a specific ISO format (same replacements as ->isoFormat()). - * @method CarbonImmutable|false createFromLocaleFormat($format, $locale, $time, $tz = null) Create a Carbon instance from a specific format and a string in a given language. - * @method CarbonImmutable|false createFromLocaleIsoFormat($format, $locale, $time, $tz = null) Create a Carbon instance from a specific ISO format and a string in a given language. - * @method CarbonImmutable createFromTime($hour = 0, $minute = 0, $second = 0, $tz = null) Create a Carbon instance from just a time. The date portion is set to today. - * @method CarbonImmutable createFromTimeString($time, $tz = null) Create a Carbon instance from a time string. The date portion is set to today. - * @method CarbonImmutable createFromTimestamp($timestamp, $tz = null) Create a Carbon instance from a timestamp and set the timezone (use default one if not specified). - * Timestamp input can be given as int, float or a string containing one or more numbers. - * @method CarbonImmutable createFromTimestampMs($timestamp, $tz = null) Create a Carbon instance from a timestamp in milliseconds. - * Timestamp input can be given as int, float or a string containing one or more numbers. - * @method CarbonImmutable createFromTimestampMsUTC($timestamp) Create a Carbon instance from a timestamp in milliseconds. - * Timestamp input can be given as int, float or a string containing one or more numbers. - * @method CarbonImmutable createFromTimestampUTC($timestamp) Create a Carbon instance from an timestamp keeping the timezone to UTC. - * Timestamp input can be given as int, float or a string containing one or more numbers. - * @method CarbonImmutable createMidnightDate($year = null, $month = null, $day = null, $tz = null) Create a Carbon instance from just a date. The time portion is set to midnight. - * @method CarbonImmutable|false createSafe($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $tz = null) Create a new safe Carbon instance from a specific date and time. - * If any of $year, $month or $day are set to null their now() values will - * be used. - * If $hour is null it will be set to its now() value and the default - * values for $minute and $second will be their now() values. - * If $hour is not null then the default values for $minute and $second - * will be 0. - * If one of the set values is not valid, an InvalidDateException - * will be thrown. - * @method CarbonInterface createStrict(?int $year = 0, ?int $month = 1, ?int $day = 1, ?int $hour = 0, ?int $minute = 0, ?int $second = 0, $tz = null) Create a new Carbon instance from a specific date and time using strict validation. - * @method CarbonImmutable disableHumanDiffOption($humanDiffOption) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use the ->settings() method. - * @method CarbonImmutable enableHumanDiffOption($humanDiffOption) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use the ->settings() method. - * @method mixed executeWithLocale($locale, $func) Set the current locale to the given, execute the passed function, reset the locale to previous one, - * then return the result of the closure (or null if the closure was void). - * @method CarbonImmutable fromSerialized($value) Create an instance from a serialized string. - * @method void genericMacro($macro, $priority = 0) Register a custom macro. - * @method array getAvailableLocales() Returns the list of internally available locales and already loaded custom locales. - * (It will ignore custom translator dynamic loading.) - * @method Language[] getAvailableLocalesInfo() Returns list of Language object for each available locale. This object allow you to get the ISO name, native - * name, region and variant of the locale. - * @method array getDays() Get the days of the week - * @method string|null getFallbackLocale() Get the fallback locale. - * @method array getFormatsToIsoReplacements() List of replacements from date() format to isoFormat(). - * @method int getHumanDiffOptions() Return default humanDiff() options (merged flags as integer). - * @method array getIsoUnits() Returns list of locale units for ISO formatting. - * @method array getLastErrors() {@inheritdoc} - * @method string getLocale() Get the current translator locale. - * @method callable|null getMacro($name) Get the raw callable macro registered globally for a given name. - * @method int getMidDayAt() get midday/noon hour - * @method Closure|CarbonImmutable getTestNow() Get the Carbon instance (real or mock) to be returned when a "now" - * instance is created. - * @method string getTimeFormatByPrecision($unitPrecision) Return a format from H:i to H:i:s.u according to given unit precision. - * @method string getTranslationMessageWith($translator, string $key, ?string $locale = null, ?string $default = null) Returns raw translation message for a given key. - * @method \Symfony\Component\Translation\TranslatorInterface getTranslator() Get the default translator instance in use. - * @method int getWeekEndsAt() Get the last day of week - * @method int getWeekStartsAt() Get the first day of week - * @method array getWeekendDays() Get weekend days - * @method bool hasFormat($date, $format) Checks if the (date)time string is in a given format. - * @method bool hasFormatWithModifiers($date, $format) Checks if the (date)time string is in a given format. - * @method bool hasMacro($name) Checks if macro is registered globally. - * @method bool hasRelativeKeywords($time) Determine if a time string will produce a relative date. - * @method bool hasTestNow() Determine if there is a valid test instance set. A valid test instance - * is anything that is not null. - * @method CarbonImmutable instance($date) Create a Carbon instance from a DateTime one. - * @method bool isImmutable() Returns true if the current class/instance is immutable. - * @method bool isModifiableUnit($unit) Returns true if a property can be changed via setter. - * @method bool isMutable() Returns true if the current class/instance is mutable. - * @method bool isStrictModeEnabled() Returns true if the strict mode is globally in use, false else. - * (It can be overridden in specific instances.) - * @method bool localeHasDiffOneDayWords($locale) Returns true if the given locale is internally supported and has words for 1-day diff (just now, yesterday, tomorrow). - * Support is considered enabled if the 3 words are translated in the given locale. - * @method bool localeHasDiffSyntax($locale) Returns true if the given locale is internally supported and has diff syntax support (ago, from now, before, after). - * Support is considered enabled if the 4 sentences are translated in the given locale. - * @method bool localeHasDiffTwoDayWords($locale) Returns true if the given locale is internally supported and has words for 2-days diff (before yesterday, after tomorrow). - * Support is considered enabled if the 2 words are translated in the given locale. - * @method bool localeHasPeriodSyntax($locale) Returns true if the given locale is internally supported and has period syntax support (X times, every X, from X, to X). - * Support is considered enabled if the 4 sentences are translated in the given locale. - * @method bool localeHasShortUnits($locale) Returns true if the given locale is internally supported and has short-units support. - * Support is considered enabled if either year, day or hour has a short variant translated. - * @method void macro($name, $macro) Register a custom macro. - * @method CarbonImmutable|null make($var) Make a Carbon instance from given variable if possible. - * Always return a new instance. Parse only strings and only these likely to be dates (skip intervals - * and recurrences). Throw an exception for invalid format, but otherwise return null. - * @method CarbonImmutable maxValue() Create a Carbon instance for the greatest supported date. - * @method CarbonImmutable minValue() Create a Carbon instance for the lowest supported date. - * @method void mixin($mixin) Mix another object into the class. - * @method CarbonImmutable parse($time = null, $tz = null) Create a carbon instance from a string. - * This is an alias for the constructor that allows better fluent syntax - * as it allows you to do Carbon::parse('Monday next week')->fn() rather - * than (new Carbon('Monday next week'))->fn(). - * @method CarbonImmutable parseFromLocale($time, $locale = null, $tz = null) Create a carbon instance from a localized string (in French, Japanese, Arabic, etc.). - * @method string pluralUnit(string $unit) Returns standardized plural of a given singular/plural unit name (in English). - * @method CarbonImmutable|false rawCreateFromFormat($format, $time, $tz = null) Create a Carbon instance from a specific format. - * @method CarbonImmutable rawParse($time = null, $tz = null) Create a carbon instance from a string. - * This is an alias for the constructor that allows better fluent syntax - * as it allows you to do Carbon::parse('Monday next week')->fn() rather - * than (new Carbon('Monday next week'))->fn(). - * @method CarbonImmutable resetMacros() Remove all macros and generic macros. - * @method void resetMonthsOverflow() @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use the ->settings() method. - * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants - * are available for quarters, years, decade, centuries, millennia (singular and plural forms). - * @method void resetToStringFormat() Reset the format used to the default when type juggling a Carbon instance to a string - * @method void resetYearsOverflow() @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use the ->settings() method. - * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants - * are available for quarters, years, decade, centuries, millennia (singular and plural forms). - * @method void serializeUsing($callback) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather transform Carbon object before the serialization. - * JSON serialize all Carbon instances using the given callback. - * @method CarbonImmutable setFallbackLocale($locale) Set the fallback locale. - * @method CarbonImmutable setHumanDiffOptions($humanDiffOptions) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use the ->settings() method. - * @method bool setLocale($locale) Set the current translator locale and indicate if the source locale file exists. - * Pass 'auto' as locale to use closest language from the current LC_TIME locale. - * @method void setMidDayAt($hour) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather consider mid-day is always 12pm, then if you need to test if it's an other - * hour, test it explicitly: - * $date->format('G') == 13 - * or to set explicitly to a given hour: - * $date->setTime(13, 0, 0, 0) - * Set midday/noon hour - * @method CarbonImmutable setTestNow($testNow = null) Set a Carbon instance (real or mock) to be returned when a "now" - * instance is created. The provided instance will be returned - * specifically under the following conditions: - * - A call to the static now() method, ex. Carbon::now() - * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null) - * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now') - * - When a string containing the desired time is passed to Carbon::parse(). - * Note the timezone parameter was left out of the examples above and - * has no affect as the mock value will be returned regardless of its value. - * Only the moment is mocked with setTestNow(), the timezone will still be the one passed - * as parameter of date_default_timezone_get() as a fallback (see setTestNowAndTimezone()). - * To clear the test instance call this method using the default - * parameter of null. - * /!\ Use this method for unit tests only. - * @method CarbonImmutable setTestNowAndTimezone($testNow = null, $tz = null) Set a Carbon instance (real or mock) to be returned when a "now" - * instance is created. The provided instance will be returned - * specifically under the following conditions: - * - A call to the static now() method, ex. Carbon::now() - * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null) - * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now') - * - When a string containing the desired time is passed to Carbon::parse(). - * It will also align default timezone (e.g. call date_default_timezone_set()) with - * the second argument or if null, with the timezone of the given date object. - * To clear the test instance call this method using the default - * parameter of null. - * /!\ Use this method for unit tests only. - * @method void setToStringFormat($format) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather let Carbon object being cast to string with DEFAULT_TO_STRING_FORMAT, and - * use other method or custom format passed to format() method if you need to dump another string - * format. - * Set the default format used when type juggling a Carbon instance to a string. - * @method void setTranslator(TranslatorInterface $translator) Set the default translator instance to use. - * @method CarbonImmutable setUtf8($utf8) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use UTF-8 language packages on every machine. - * Set if UTF8 will be used for localized date/time. - * @method void setWeekEndsAt($day) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * Use $weekStartsAt optional parameter instead when using startOfWeek, floorWeek, ceilWeek - * or roundWeek method. You can also use the 'first_day_of_week' locale setting to change the - * start of week according to current locale selected and implicitly the end of week. - * Set the last day of week - * @method void setWeekStartsAt($day) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * Use $weekEndsAt optional parameter instead when using endOfWeek method. You can also use the - * 'first_day_of_week' locale setting to change the start of week according to current locale - * selected and implicitly the end of week. - * Set the first day of week - * @method void setWeekendDays($days) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather consider week-end is always saturday and sunday, and if you have some custom - * week-end days to handle, give to those days an other name and create a macro for them: - * ``` - * Carbon::macro('isDayOff', function ($date) { - * return $date->isSunday() || $date->isMonday(); - * }); - * Carbon::macro('isNotDayOff', function ($date) { - * return !$date->isDayOff(); - * }); - * if ($someDate->isDayOff()) ... - * if ($someDate->isNotDayOff()) ... - * // Add 5 not-off days - * $count = 5; - * while ($someDate->isDayOff() || ($count-- > 0)) { - * $someDate->addDay(); - * } - * ``` - * Set weekend days - * @method bool shouldOverflowMonths() Get the month overflow global behavior (can be overridden in specific instances). - * @method bool shouldOverflowYears() Get the month overflow global behavior (can be overridden in specific instances). - * @method string singularUnit(string $unit) Returns standardized singular of a given singular/plural unit name (in English). - * @method CarbonImmutable today($tz = null) Create a Carbon instance for today. - * @method CarbonImmutable tomorrow($tz = null) Create a Carbon instance for tomorrow. - * @method string translateTimeString($timeString, $from = null, $to = null, $mode = CarbonInterface::TRANSLATE_ALL) Translate a time string from a locale to an other. - * @method string translateWith(TranslatorInterface $translator, string $key, array $parameters = [], $number = null) Translate using translation string or callback available. - * @method void useMonthsOverflow($monthsOverflow = true) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use the ->settings() method. - * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants - * are available for quarters, years, decade, centuries, millennia (singular and plural forms). - * @method CarbonImmutable useStrictMode($strictModeEnabled = true) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use the ->settings() method. - * @method void useYearsOverflow($yearsOverflow = true) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use the ->settings() method. - * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants - * are available for quarters, years, decade, centuries, millennia (singular and plural forms). - * @method mixed withTestNow($testNow, $callback) Temporarily sets a static date to be used within the callback. - * Using setTestNow to set the date, executing the callback, then - * clearing the test instance. - * /!\ Use this method for unit tests only. - * @method CarbonImmutable yesterday($tz = null) Create a Carbon instance for yesterday. + * @method bool canBeCreatedFromFormat(?string $date, string $format) Checks if the (date)time string is in a given format and valid to create a + * new instance. + * @method ?CarbonImmutable create($year = 0, $month = 1, $day = 1, $hour = 0, $minute = 0, $second = 0, $timezone = null) Create a new Carbon instance from a specific date and time. + * If any of $year, $month or $day are set to null their now() values will + * be used. + * If $hour is null it will be set to its now() value and the default + * values for $minute and $second will be their now() values. + * If $hour is not null then the default values for $minute and $second + * will be 0. + * @method CarbonImmutable createFromDate($year = null, $month = null, $day = null, $timezone = null) Create a Carbon instance from just a date. The time portion is set to now. + * @method ?CarbonImmutable createFromFormat($format, $time, $timezone = null) Create a Carbon instance from a specific format. + * @method ?CarbonImmutable createFromIsoFormat(string $format, string $time, $timezone = null, ?string $locale = 'en', ?TranslatorInterface $translator = null) Create a Carbon instance from a specific ISO format (same replacements as ->isoFormat()). + * @method ?CarbonImmutable createFromLocaleFormat(string $format, string $locale, string $time, $timezone = null) Create a Carbon instance from a specific format and a string in a given language. + * @method ?CarbonImmutable createFromLocaleIsoFormat(string $format, string $locale, string $time, $timezone = null) Create a Carbon instance from a specific ISO format and a string in a given language. + * @method CarbonImmutable createFromTime($hour = 0, $minute = 0, $second = 0, $timezone = null) Create a Carbon instance from just a time. The date portion is set to today. + * @method CarbonImmutable createFromTimeString(string $time, DateTimeZone|string|int|null $timezone = null) Create a Carbon instance from a time string. The date portion is set to today. + * @method CarbonImmutable createFromTimestamp(string|int|float $timestamp, DateTimeZone|string|int|null $timezone = null) Create a Carbon instance from a timestamp and set the timezone (UTC by default). + * Timestamp input can be given as int, float or a string containing one or more numbers. + * @method CarbonImmutable createFromTimestampMs(string|int|float $timestamp, DateTimeZone|string|int|null $timezone = null) Create a Carbon instance from a timestamp in milliseconds. + * Timestamp input can be given as int, float or a string containing one or more numbers. + * @method CarbonImmutable createFromTimestampMsUTC($timestamp) Create a Carbon instance from a timestamp in milliseconds. + * Timestamp input can be given as int, float or a string containing one or more numbers. + * @method CarbonImmutable createFromTimestampUTC(string|int|float $timestamp) Create a Carbon instance from a timestamp keeping the timezone to UTC. + * Timestamp input can be given as int, float or a string containing one or more numbers. + * @method CarbonImmutable createMidnightDate($year = null, $month = null, $day = null, $timezone = null) Create a Carbon instance from just a date. The time portion is set to midnight. + * @method ?CarbonImmutable createSafe($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $timezone = null) Create a new safe Carbon instance from a specific date and time. + * If any of $year, $month or $day are set to null their now() values will + * be used. + * If $hour is null it will be set to its now() value and the default + * values for $minute and $second will be their now() values. + * If $hour is not null then the default values for $minute and $second + * will be 0. + * If one of the set values is not valid, an InvalidDateException + * will be thrown. + * @method CarbonImmutable createStrict(?int $year = 0, ?int $month = 1, ?int $day = 1, ?int $hour = 0, ?int $minute = 0, ?int $second = 0, $timezone = null) Create a new Carbon instance from a specific date and time using strict validation. + * @method mixed executeWithLocale(string $locale, callable $func) Set the current locale to the given, execute the passed function, reset the locale to previous one, + * then return the result of the closure (or null if the closure was void). + * @method CarbonImmutable fromSerialized($value) Create an instance from a serialized string. + * @method array getAvailableLocales() Returns the list of internally available locales and already loaded custom locales. + * (It will ignore custom translator dynamic loading.) + * @method Language[] getAvailableLocalesInfo() Returns list of Language object for each available locale. This object allow you to get the ISO name, native + * name, region and variant of the locale. + * @method array getDays() Get the days of the week. + * @method ?string getFallbackLocale() Get the fallback locale. + * @method array getFormatsToIsoReplacements() List of replacements from date() format to isoFormat(). + * @method array getIsoUnits() Returns list of locale units for ISO formatting. + * @method array|false getLastErrors() {@inheritdoc} + * @method string getLocale() Get the current translator locale. + * @method int getMidDayAt() get midday/noon hour + * @method string getTimeFormatByPrecision(string $unitPrecision) Return a format from H:i to H:i:s.u according to given unit precision. + * @method string|Closure|null getTranslationMessageWith($translator, string $key, ?string $locale = null, ?string $default = null) Returns raw translation message for a given key. + * @method int getWeekEndsAt(?string $locale = null) Get the last day of week. + * @method int getWeekStartsAt(?string $locale = null) Get the first day of week. + * @method bool hasRelativeKeywords(?string $time) Determine if a time string will produce a relative date. + * @method CarbonImmutable instance(DateTimeInterface $date) Create a Carbon instance from a DateTime one. + * @method bool isImmutable() Returns true if the current class/instance is immutable. + * @method bool isModifiableUnit($unit) Returns true if a property can be changed via setter. + * @method bool isMutable() Returns true if the current class/instance is mutable. + * @method bool localeHasDiffOneDayWords(string $locale) Returns true if the given locale is internally supported and has words for 1-day diff (just now, yesterday, tomorrow). + * Support is considered enabled if the 3 words are translated in the given locale. + * @method bool localeHasDiffSyntax(string $locale) Returns true if the given locale is internally supported and has diff syntax support (ago, from now, before, after). + * Support is considered enabled if the 4 sentences are translated in the given locale. + * @method bool localeHasDiffTwoDayWords(string $locale) Returns true if the given locale is internally supported and has words for 2-days diff (before yesterday, after tomorrow). + * Support is considered enabled if the 2 words are translated in the given locale. + * @method bool localeHasPeriodSyntax($locale) Returns true if the given locale is internally supported and has period syntax support (X times, every X, from X, to X). + * Support is considered enabled if the 4 sentences are translated in the given locale. + * @method bool localeHasShortUnits(string $locale) Returns true if the given locale is internally supported and has short-units support. + * Support is considered enabled if either year, day or hour has a short variant translated. + * @method ?CarbonImmutable make($var, DateTimeZone|string|null $timezone = null) Make a Carbon instance from given variable if possible. + * Always return a new instance. Parse only strings and only these likely to be dates (skip intervals + * and recurrences). Throw an exception for invalid format, but otherwise return null. + * @method void mixin(object|string $mixin) Mix another object into the class. + * @method CarbonImmutable parse(DateTimeInterface|WeekDay|Month|string|int|float|null $time, DateTimeZone|string|int|null $timezone = null) Create a carbon instance from a string. + * This is an alias for the constructor that allows better fluent syntax + * as it allows you to do Carbon::parse('Monday next week')->fn() rather + * than (new Carbon('Monday next week'))->fn(). + * @method CarbonImmutable parseFromLocale(string $time, ?string $locale = null, DateTimeZone|string|int|null $timezone = null) Create a carbon instance from a localized string (in French, Japanese, Arabic, etc.). + * @method string pluralUnit(string $unit) Returns standardized plural of a given singular/plural unit name (in English). + * @method ?CarbonImmutable rawCreateFromFormat(string $format, string $time, $timezone = null) Create a Carbon instance from a specific format. + * @method CarbonImmutable rawParse(DateTimeInterface|WeekDay|Month|string|int|float|null $time, DateTimeZone|string|int|null $timezone = null) Create a carbon instance from a string. + * This is an alias for the constructor that allows better fluent syntax + * as it allows you to do Carbon::parse('Monday next week')->fn() rather + * than (new Carbon('Monday next week'))->fn(). + * @method void setFallbackLocale(string $locale) Set the fallback locale. + * @method void setLocale(string $locale) Set the current translator locale and indicate if the source locale file exists. + * Pass 'auto' as locale to use the closest language to the current LC_TIME locale. + * @method void setMidDayAt($hour) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather consider mid-day is always 12pm, then if you need to test if it's an other + * hour, test it explicitly: + * $date->format('G') == 13 + * or to set explicitly to a given hour: + * $date->setTime(13, 0, 0, 0) + * Set midday/noon hour + * @method string singularUnit(string $unit) Returns standardized singular of a given singular/plural unit name (in English). + * @method CarbonImmutable today(DateTimeZone|string|int|null $timezone = null) Create a Carbon instance for today. + * @method CarbonImmutable tomorrow(DateTimeZone|string|int|null $timezone = null) Create a Carbon instance for tomorrow. + * @method string translateTimeString(string $timeString, ?string $from = null, ?string $to = null, int $mode = CarbonInterface::TRANSLATE_ALL) Translate a time string from a locale to an other. + * @method string translateWith(TranslatorInterface $translator, string $key, array $parameters = [], $number = null) Translate using translation string or callback available. + * @method CarbonImmutable yesterday(DateTimeZone|string|int|null $timezone = null) Create a Carbon instance for yesterday. * * */ class FactoryImmutable extends Factory implements ClockInterface { - protected $className = CarbonImmutable::class; + protected string $className = CarbonImmutable::class; + + private static ?self $defaultInstance = null; + + private static ?WrapperClock $currentClock = null; + + /** + * @internal Instance used for static calls, such as Carbon::getTranslator(), CarbonImmutable::setTestNow(), etc. + */ + public static function getDefaultInstance(): self + { + return self::$defaultInstance ??= new self(); + } + + /** + * @internal Instance used for static calls possibly called by non-static methods. + */ + public static function getInstance(): Factory + { + return self::$currentClock?->getFactory() ?? self::getDefaultInstance(); + } + + /** + * @internal Set instance before creating new dates. + */ + public static function setCurrentClock(ClockInterface|Factory|DateTimeInterface|null $currentClock): void + { + if ($currentClock && !($currentClock instanceof WrapperClock)) { + $currentClock = new WrapperClock($currentClock); + } + + self::$currentClock = $currentClock; + } + + /** + * @internal Instance used to link new object to their factory creator. + */ + public static function getCurrentClock(): ?WrapperClock + { + return self::$currentClock; + } /** * Get a Carbon instance for the current date and time. - * - * @param DateTimeZone|string|int|null $tz - * - * @return CarbonImmutable */ - public function now($tz = null): DateTimeImmutable + public function now(DateTimeZone|string|int|null $timezone = null): CarbonImmutable { - $className = $this->className; + return $this->__call('now', [$timezone]); + } + + public function sleep(int|float $seconds): void + { + if ($this->hasTestNow()) { + $this->setTestNow($this->getTestNow()->avoidMutation()->addSeconds($seconds)); + + return; + } - return new $className(null, $tz); + (new NativeClock('UTC'))->sleep($seconds); } } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/aa_ET.php b/vendor/nesbot/carbon/src/Carbon/Lang/aa_ET.php index e55e591b5..b6f7d0b3a 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/aa_ET.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/aa_ET.php @@ -22,6 +22,7 @@ 'weekdays' => ['Acaada', 'Etleeni', 'Talaata', 'Arbaqa', 'Kamiisi', 'Gumqata', 'Sabti'], 'weekdays_short' => ['Aca', 'Etl', 'Tal', 'Arb', 'Kam', 'Gum', 'Sab'], 'weekdays_min' => ['Aca', 'Etl', 'Tal', 'Arb', 'Kam', 'Gum', 'Sab'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['saaku', 'carra'], ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/af.php b/vendor/nesbot/carbon/src/Carbon/Lang/af.php index 27771d7aa..87592fe1b 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/af.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/af.php @@ -64,9 +64,7 @@ 'lastWeek' => '[Laas] dddd [om] LT', 'sameElse' => 'L', ], - 'ordinal' => function ($number) { - return $number.(($number === 1 || $number === 8 || $number >= 20) ? 'ste' : 'de'); - }, + 'ordinal' => static fn ($number) => $number.(($number === 1 || $number === 8 || $number >= 20) ? 'ste' : 'de'), 'meridiem' => ['VM', 'NM'], 'months' => ['Januarie', 'Februarie', 'Maart', 'April', 'Mei', 'Junie', 'Julie', 'Augustus', 'September', 'Oktober', 'November', 'Desember'], 'months_short' => ['Jan', 'Feb', 'Mrt', 'Apr', 'Mei', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Des'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/am_ET.php b/vendor/nesbot/carbon/src/Carbon/Lang/am_ET.php index ece80621a..7cc676b37 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/am_ET.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/am_ET.php @@ -22,6 +22,7 @@ 'weekdays' => ['እሑድ', 'ሰኞ', 'ማክሰኞ', 'ረቡዕ', 'ሐሙስ', 'ዓርብ', 'ቅዳሜ'], 'weekdays_short' => ['እሑድ', 'ሰኞ ', 'ማክሰ', 'ረቡዕ', 'ሐሙስ', 'ዓርብ', 'ቅዳሜ'], 'weekdays_min' => ['እሑድ', 'ሰኞ ', 'ማክሰ', 'ረቡዕ', 'ሐሙስ', 'ዓርብ', 'ቅዳሜ'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['ጡዋት', 'ከሰዓት'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/anp_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/anp_IN.php index 11069be3f..00baa980a 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/anp_IN.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/anp_IN.php @@ -22,6 +22,7 @@ 'weekdays' => ['रविवार', 'सोमवार', 'मंगलवार', 'बुधवार', 'बृहस्पतिवार', 'शुक्रवार', 'शनिवार'], 'weekdays_short' => ['रवि', 'सोम', 'मंगल', 'बुध', 'बृहस्पति', 'शुक्र', 'शनि'], 'weekdays_min' => ['रवि', 'सोम', 'मंगल', 'बुध', 'बृहस्पति', 'शुक्र', 'शनि'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['पूर्वाह्न', 'अपराह्न'], ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/as_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/as_IN.php index 5fbc3dba3..f2499abf2 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/as_IN.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/as_IN.php @@ -22,6 +22,7 @@ 'weekdays' => ['দেওবাৰ', 'সোমবাৰ', 'মঙ্গলবাৰ', 'বুধবাৰ', 'বৃহষ্পতিবাৰ', 'শুক্ৰবাৰ', 'শনিবাৰ'], 'weekdays_short' => ['দেও', 'সোম', 'মঙ্গল', 'বুধ', 'বৃহষ্পতি', 'শুক্ৰ', 'শনি'], 'weekdays_min' => ['দেও', 'সোম', 'মঙ্গল', 'বুধ', 'বৃহষ্পতি', 'শুক্ৰ', 'শনি'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['পূৰ্ব্বাহ্ন', 'অপৰাহ্ন'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ayc_PE.php b/vendor/nesbot/carbon/src/Carbon/Lang/ayc_PE.php index ff18504f0..22374e007 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ayc_PE.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ayc_PE.php @@ -22,6 +22,7 @@ 'weekdays' => ['tuminku', 'lunisa', 'martisa', 'mirkulisa', 'juywisa', 'wirnisa', 'sawäru'], 'weekdays_short' => ['tum', 'lun', 'mar', 'mir', 'juy', 'wir', 'saw'], 'weekdays_min' => ['tum', 'lun', 'mar', 'mir', 'juy', 'wir', 'saw'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['VM', 'NM'], ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/az.php b/vendor/nesbot/carbon/src/Carbon/Lang/az.php index 1e92106df..1d71d1da2 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/az.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/az.php @@ -18,29 +18,30 @@ * - Orxan * - Şəhriyar İmanov * - Baran Şengül + * - Novruz Rahimov */ return [ 'year' => ':count il', - 'a_year' => '{1}bir il|]1,Inf[:count il', + 'a_year' => '{1}bir il|[-Inf,Inf]:count il', 'y' => ':count il', 'month' => ':count ay', - 'a_month' => '{1}bir ay|]1,Inf[:count ay', + 'a_month' => '{1}bir ay|[-Inf,Inf]:count ay', 'm' => ':count ay', 'week' => ':count həftə', - 'a_week' => '{1}bir həftə|]1,Inf[:count həftə', + 'a_week' => '{1}bir həftə|[-Inf,Inf]:count həftə', 'w' => ':count h.', 'day' => ':count gün', - 'a_day' => '{1}bir gün|]1,Inf[:count gün', + 'a_day' => '{1}bir gün|[-Inf,Inf]:count gün', 'd' => ':count g.', 'hour' => ':count saat', - 'a_hour' => '{1}bir saat|]1,Inf[:count saat', - 'h' => ':count saat', - 'minute' => ':count d.', - 'a_minute' => '{1}bir dəqiqə|]1,Inf[:count dəqiqə', - 'min' => ':count dəqiqə', - 'second' => ':count san.', - 'a_second' => '{1}birneçə saniyə|]1,Inf[:count saniyə', - 's' => ':count saniyə', + 'a_hour' => '{1}bir saat|[-Inf,Inf]:count saat', + 'h' => ':count s.', + 'minute' => ':count dəqiqə', + 'a_minute' => '{1}bir dəqiqə|[-Inf,Inf]:count dəqiqə', + 'min' => ':count d.', + 'second' => ':count saniyə', + 'a_second' => '{1}birneçə saniyə|[-Inf,Inf]:count saniyə', + 's' => ':count san.', 'ago' => ':time əvvəl', 'from_now' => ':time sonra', 'after' => ':time sonra', @@ -73,7 +74,7 @@ 'lastWeek' => '[keçən həftə] dddd [saat] LT', 'sameElse' => 'L', ], - 'ordinal' => function ($number) { + 'ordinal' => static function ($number) { if ($number === 0) { // special case for zero return "$number-ıncı"; } @@ -103,7 +104,7 @@ return $number.($suffixes[$lastDigit] ?? $suffixes[$number % 100 - $lastDigit] ?? $suffixes[$number >= 100 ? 100 : -1] ?? ''); }, - 'meridiem' => function ($hour) { + 'meridiem' => static function ($hour) { if ($hour < 4) { return 'gecə'; } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/be.php b/vendor/nesbot/carbon/src/Carbon/Lang/be.php index ee736365a..06f4043dc 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/be.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/be.php @@ -117,39 +117,27 @@ 'nextDay' => '[Заўтра ў] LT', 'nextWeek' => '[У] dddd [ў] LT', 'lastDay' => '[Учора ў] LT', - 'lastWeek' => function (CarbonInterface $current) { - switch ($current->dayOfWeek) { - case 1: - case 2: - case 4: - return '[У мінулы] dddd [ў] LT'; - default: - return '[У мінулую] dddd [ў] LT'; - } + 'lastWeek' => static fn (CarbonInterface $current) => match ($current->dayOfWeek) { + 1, 2, 4 => '[У мінулы] dddd [ў] LT', + default => '[У мінулую] dddd [ў] LT', }, 'sameElse' => 'L', ], - 'ordinal' => function ($number, $period) { - switch ($period) { - case 'M': - case 'd': - case 'DDD': - case 'w': - case 'W': - return ($number % 10 === 2 || $number % 10 === 3) && ($number % 100 !== 12 && $number % 100 !== 13) ? $number.'-і' : $number.'-ы'; - case 'D': - return $number.'-га'; - default: - return $number; - } + 'ordinal' => static fn ($number, $period) => match ($period) { + 'M', 'd', 'DDD', 'w', 'W' => ($number % 10 === 2 || $number % 10 === 3) && + ($number % 100 !== 12 && $number % 100 !== 13) ? $number.'-і' : $number.'-ы', + 'D' => $number.'-га', + default => $number, }, - 'meridiem' => function ($hour) { + 'meridiem' => static function ($hour) { if ($hour < 4) { return 'ночы'; } + if ($hour < 12) { return 'раніцы'; } + if ($hour < 17) { return 'дня'; } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bg.php b/vendor/nesbot/carbon/src/Carbon/Lang/bg.php index f76807403..c793ab7c5 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/bg.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bg.php @@ -66,19 +66,13 @@ 'nextDay' => '[Утре в] LT', 'nextWeek' => 'dddd [в] LT', 'lastDay' => '[Вчера в] LT', - 'lastWeek' => function (CarbonInterface $current) { - switch ($current->dayOfWeek) { - case 0: - case 3: - case 6: - return '[В изминалата] dddd [в] LT'; - default: - return '[В изминалия] dddd [в] LT'; - } + 'lastWeek' => static fn (CarbonInterface $current) => match ($current->dayOfWeek) { + 0, 3, 6 => '[В изминалата] dddd [в] LT', + default => '[В изминалия] dddd [в] LT', }, 'sameElse' => 'L', ], - 'ordinal' => function ($number) { + 'ordinal' => static function ($number) { $lastDigit = $number % 10; $last2Digits = $number % 100; if ($number === 0) { diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bhb_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/bhb_IN.php index ab557cbf3..fadbf77fb 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/bhb_IN.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bhb_IN.php @@ -22,5 +22,6 @@ 'weekdays' => ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], 'weekdays_short' => ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], 'weekdays_min' => ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bho_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/bho_IN.php index bc54f3631..2f4cdfa02 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/bho_IN.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bho_IN.php @@ -22,6 +22,7 @@ 'weekdays' => ['रविवार', 'सोमवार', 'मंगलवार', 'बुधवार', 'गुरुवार', 'शुक्रवार', 'शनिवार'], 'weekdays_short' => ['रवि', 'सोम', 'मंगल', 'बुध', 'गुरु', 'शुक्र', 'शनि'], 'weekdays_min' => ['रवि', 'सोम', 'मंगल', 'बुध', 'गुरु', 'शुक्र', 'शनि'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['पूर्वाह्न', 'अपराह्न'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bi_VU.php b/vendor/nesbot/carbon/src/Carbon/Lang/bi_VU.php index 1fe777056..15d335c89 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/bi_VU.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bi_VU.php @@ -14,6 +14,7 @@ * - Samsung Electronics Co., Ltd. akhilesh.k@samsung.com & maninder1.s@samsung.com */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'formats' => [ 'L' => 'dddd DD MMM YYYY', ], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bn.php b/vendor/nesbot/carbon/src/Carbon/Lang/bn.php index 8e147899b..663cf25a5 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/bn.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bn.php @@ -69,7 +69,7 @@ 'lastWeek' => '[গত] dddd, LT', 'sameElse' => 'L', ], - 'meridiem' => function ($hour) { + 'meridiem' => static function ($hour) { if ($hour < 4) { return 'রাত'; } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bo.php b/vendor/nesbot/carbon/src/Carbon/Lang/bo.php index 99e1bf4c9..21cc90d1d 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/bo.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bo.php @@ -15,13 +15,20 @@ * - JD Isaacks */ return [ - 'year' => '{1}ལོ་གཅིག|]1,Inf[:count ལོ', - 'month' => '{1}ཟླ་བ་གཅིག|]1,Inf[:count ཟླ་བ', - 'week' => ':count བདུན་ཕྲག', - 'day' => '{1}ཉིན་གཅིག|]1,Inf[:count ཉིན་', - 'hour' => '{1}ཆུ་ཚོད་གཅིག|]1,Inf[:count ཆུ་ཚོད', - 'minute' => '{1}སྐར་མ་གཅིག|]1,Inf[:count སྐར་མ', - 'second' => '{1}ལམ་སང|]1,Inf[:count སྐར་ཆ།', + 'year' => 'ལོ:count', + 'a_year' => '{1}ལོ་གཅིག|[-Inf,Inf]ལོ:count', + 'month' => 'ཟླ་བ:count', + 'a_month' => '{1}ཟླ་བ་གཅིག|[-Inf,Inf]ཟླ་བ:count', + 'week' => 'གཟའ་འཁོར་:count', + 'a_week' => 'གཟའ་འཁོར་གཅིག', + 'day' => 'ཉིན:count་', + 'a_day' => '{1}ཉིན་གཅིག|[-Inf,Inf]ཉིན:count', + 'hour' => 'ཆུ་ཚོད:count', + 'a_hour' => '{1}ཆུ་ཚོད་གཅིག|[-Inf,Inf]ཆུ་ཚོད:count', + 'minute' => 'སྐར་མ་:count', + 'a_minute' => '{1}སྐར་མ་གཅིག|[-Inf,Inf]སྐར་མ་:count', + 'second' => 'སྐར་ཆ:count', + 'a_second' => '{01}ལམ་སང|[-Inf,Inf]སྐར་ཆ:count', 'ago' => ':time སྔན་ལ', 'from_now' => ':time ལ་', 'diff_yesterday' => 'ཁ་སང', @@ -43,7 +50,7 @@ 'lastWeek' => '[བདུན་ཕྲག་མཐའ་མ] dddd, LT', 'sameElse' => 'L', ], - 'meridiem' => function ($hour) { + 'meridiem' => static function ($hour) { if ($hour < 4) { return 'མཚན་མོ'; } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/br.php b/vendor/nesbot/carbon/src/Carbon/Lang/br.php index 583472fb0..cdc0545b1 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/br.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/br.php @@ -55,9 +55,7 @@ 'lastWeek' => 'dddd [paset da] LT', 'sameElse' => 'L', ], - 'ordinal' => function ($number) { - return $number.($number === 1 ? 'añ' : 'vet'); - }, + 'ordinal' => static fn ($number) => $number.($number === 1 ? 'añ' : 'vet'), 'months' => ['Genver', 'C\'hwevrer', 'Meurzh', 'Ebrel', 'Mae', 'Mezheven', 'Gouere', 'Eost', 'Gwengolo', 'Here', 'Du', 'Kerzu'], 'months_short' => ['Gen', 'C\'hwe', 'Meu', 'Ebr', 'Mae', 'Eve', 'Gou', 'Eos', 'Gwe', 'Her', 'Du', 'Ker'], 'weekdays' => ['Sul', 'Lun', 'Meurzh', 'Merc\'her', 'Yaou', 'Gwener', 'Sadorn'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/brx_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/brx_IN.php index 2d80ced20..e678aa966 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/brx_IN.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/brx_IN.php @@ -22,6 +22,7 @@ 'weekdays' => ['रबिबार', 'सोबार', 'मंगलबार', 'बुदबार', 'बिसथिबार', 'सुखुरबार', 'सुनिबार'], 'weekdays_short' => ['रबि', 'सम', 'मंगल', 'बुद', 'बिसथि', 'सुखुर', 'सुनि'], 'weekdays_min' => ['रबि', 'सम', 'मंगल', 'बुद', 'बिसथि', 'सुखुर', 'सुनि'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['फुं.', 'बेलासे.'], ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bs.php b/vendor/nesbot/carbon/src/Carbon/Lang/bs.php index e5d68083e..52b6d12aa 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/bs.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bs.php @@ -17,6 +17,7 @@ * - shaishavgandhi05 * - Serhan Apaydın * - JD Isaacks + * - Ademir Šehić */ use Carbon\CarbonInterface; @@ -26,8 +27,8 @@ 'y' => ':count godina|:count godine|:count godina', 'month' => ':count mjesec|:count mjeseca|:count mjeseci', 'm' => ':count mjesec|:count mjeseca|:count mjeseci', - 'week' => ':count sedmice|:count sedmicu|:count sedmica', - 'w' => ':count sedmice|:count sedmicu|:count sedmica', + 'week' => ':count sedmica|:count sedmice|:count sedmica', + 'w' => ':count sedmica|:count sedmice|:count sedmica', 'day' => ':count dan|:count dana|:count dana', 'd' => ':count dan|:count dana|:count dana', 'hour' => ':count sat|:count sata|:count sati', @@ -36,10 +37,17 @@ 'min' => ':count minut|:count minuta|:count minuta', 'second' => ':count sekund|:count sekunda|:count sekundi', 's' => ':count sekund|:count sekunda|:count sekundi', + 'ago' => 'prije :time', 'from_now' => 'za :time', 'after' => 'nakon :time', 'before' => ':time ranije', + + 'year_ago' => ':count godinu|:count godine|:count godina', + 'year_from_now' => ':count godinu|:count godine|:count godina', + 'week_ago' => ':count sedmicu|:count sedmice|:count sedmica', + 'week_from_now' => ':count sedmicu|:count sedmice|:count sedmica', + 'diff_now' => 'sada', 'diff_today' => 'danas', 'diff_today_regexp' => 'danas(?:\\s+u)?', @@ -58,29 +66,17 @@ 'calendar' => [ 'sameDay' => '[danas u] LT', 'nextDay' => '[sutra u] LT', - 'nextWeek' => function (CarbonInterface $current) { - switch ($current->dayOfWeek) { - case 0: - return '[u] [nedjelju] [u] LT'; - case 3: - return '[u] [srijedu] [u] LT'; - case 6: - return '[u] [subotu] [u] LT'; - default: - return '[u] dddd [u] LT'; - } + 'nextWeek' => static fn (CarbonInterface $current) => match ($current->dayOfWeek) { + 0 => '[u] [nedjelju] [u] LT', + 3 => '[u] [srijedu] [u] LT', + 6 => '[u] [subotu] [u] LT', + default => '[u] dddd [u] LT', }, 'lastDay' => '[jučer u] LT', - 'lastWeek' => function (CarbonInterface $current) { - switch ($current->dayOfWeek) { - case 0: - case 3: - return '[prošlu] dddd [u] LT'; - case 6: - return '[prošle] [subote] [u] LT'; - default: - return '[prošli] dddd [u] LT'; - } + 'lastWeek' => static fn (CarbonInterface $current) => match ($current->dayOfWeek) { + 0, 3 => '[prošlu] dddd [u] LT', + 6 => '[prošle] [subote] [u] LT', + default => '[prošli] dddd [u] LT', }, 'sameElse' => 'L', ], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ca.php b/vendor/nesbot/carbon/src/Carbon/Lang/ca.php index b8b19946b..824c9ce7a 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ca.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ca.php @@ -72,24 +72,24 @@ 'LLLL' => 'dddd D MMMM [de] YYYY [a les] H:mm', ], 'calendar' => [ - 'sameDay' => function (CarbonInterface $current) { + 'sameDay' => static function (CarbonInterface $current) { return '[avui a '.($current->hour !== 1 ? 'les' : 'la').'] LT'; }, - 'nextDay' => function (CarbonInterface $current) { + 'nextDay' => static function (CarbonInterface $current) { return '[demà a '.($current->hour !== 1 ? 'les' : 'la').'] LT'; }, - 'nextWeek' => function (CarbonInterface $current) { + 'nextWeek' => static function (CarbonInterface $current) { return 'dddd [a '.($current->hour !== 1 ? 'les' : 'la').'] LT'; }, - 'lastDay' => function (CarbonInterface $current) { + 'lastDay' => static function (CarbonInterface $current) { return '[ahir a '.($current->hour !== 1 ? 'les' : 'la').'] LT'; }, - 'lastWeek' => function (CarbonInterface $current) { + 'lastWeek' => static function (CarbonInterface $current) { return '[el] dddd [passat a '.($current->hour !== 1 ? 'les' : 'la').'] LT'; }, 'sameElse' => 'L', ], - 'ordinal' => function ($number, $period) { + 'ordinal' => static function ($number, $period) { return $number.( ($period === 'w' || $period === 'W') ? 'a' : ( ($number === 1) ? 'r' : ( diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ccp.php b/vendor/nesbot/carbon/src/Carbon/Lang/ccp.php index 99c1dcacd..b536d4bfe 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ccp.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ccp.php @@ -24,4 +24,5 @@ 'LLL' => 'D MMMM, YYYY h:mm a', 'LLLL' => 'dddd, D MMMM, YYYY h:mm a', ], + 'first_day_of_week' => 0, ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/chr_US.php b/vendor/nesbot/carbon/src/Carbon/Lang/chr_US.php index 371353ef8..3fb022198 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/chr_US.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/chr_US.php @@ -22,6 +22,7 @@ 'weekdays' => ['ᎤᎾᏙᏓᏆᏍᎬ', 'ᎤᎾᏙᏓᏉᏅᎯ', 'ᏔᎵᏁᎢᎦ', 'ᏦᎢᏁᎢᎦ', 'ᏅᎩᏁᎢᎦ', 'ᏧᎾᎩᎶᏍᏗ', 'ᎤᎾᏙᏓᏈᏕᎾ'], 'weekdays_short' => ['ᏆᏍᎬ', 'ᏉᏅᎯ', 'ᏔᎵᏁ', 'ᏦᎢᏁ', 'ᏅᎩᏁ', 'ᏧᎾᎩ', 'ᏈᏕᎾ'], 'weekdays_min' => ['ᏆᏍᎬ', 'ᏉᏅᎯ', 'ᏔᎵᏁ', 'ᏦᎢᏁ', 'ᏅᎩᏁ', 'ᏧᎾᎩ', 'ᏈᏕᎾ'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['ᏌᎾᎴ', 'ᏒᎯᏱᎢᏗᏢ', 'ꮜꮎꮄ', 'ꮢꭿᏹꭲꮧꮲ'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ckb.php b/vendor/nesbot/carbon/src/Carbon/Lang/ckb.php index acf4dc280..35ac60a95 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ckb.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ckb.php @@ -12,20 +12,21 @@ /* * Authors: * - Swara Mohammed + * - Kawan Pshtiwan */ $months = [ - 'ڕێبەندان', - 'ڕەشەمە', - 'نەورۆز', - 'گوڵان', - 'جۆزەردان', - 'پوشپەڕ', - 'گەلاوێژ', - 'خەرمانان', - 'ڕەزبەر', - 'گەڵاڕێزان', - 'سەرماوەرز', - 'بەفرانبار', + 'کانونی دووەم', + 'شوبات', + 'ئازار', + 'نیسان', + 'ئایار', + 'حوزەیران', + 'تەمموز', + 'ئاب', + 'ئەیلوول', + 'تشرینی یەکەم', + 'تشرینی دووەم', + 'کانونی یەکەم', ]; return [ diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cmn_TW.php b/vendor/nesbot/carbon/src/Carbon/Lang/cmn_TW.php index 7e43f9de3..eee9c806b 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/cmn_TW.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/cmn_TW.php @@ -14,6 +14,7 @@ * - bug-glibc-locales@gnu.org */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'formats' => [ 'L' => 'YYYY年MM月DD號', ], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cs.php b/vendor/nesbot/carbon/src/Carbon/Lang/cs.php index c01e3ccc8..9530d363d 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/cs.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/cs.php @@ -104,6 +104,7 @@ 'months' => ['ledna', 'února', 'března', 'dubna', 'května', 'června', 'července', 'srpna', 'září', 'října', 'listopadu', 'prosince'], 'months_standalone' => ['leden', 'únor', 'březen', 'duben', 'květen', 'červen', 'červenec', 'srpen', 'září', 'říjen', 'listopad', 'prosinec'], 'months_short' => ['led', 'úno', 'bře', 'dub', 'kvě', 'čvn', 'čvc', 'srp', 'zář', 'říj', 'lis', 'pro'], + 'months_regexp' => '/(DD?o?\.?(\[[^\[\]]*\]|\s)+MMMM?|L{2,4}|l{2,4})/', 'weekdays' => ['neděle', 'pondělí', 'úterý', 'středa', 'čtvrtek', 'pátek', 'sobota'], 'weekdays_short' => ['ned', 'pon', 'úte', 'stř', 'čtv', 'pát', 'sob'], 'weekdays_min' => ['ne', 'po', 'út', 'st', 'čt', 'pá', 'so'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cv.php b/vendor/nesbot/carbon/src/Carbon/Lang/cv.php index 8aeb73aa8..fe7696853 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/cv.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/cv.php @@ -31,8 +31,8 @@ 'second' => ':count ҫеккунт', 'a_second' => '{1}пӗр-ик ҫеккунт|:count ҫеккунт', 'ago' => ':time каялла', - 'from_now' => function ($time) { - return $time.(preg_match('/сехет$/u', $time) ? 'рен' : (preg_match('/ҫул/u', $time) ? 'тан' : 'ран')); + 'from_now' => static function ($time) { + return $time.(preg_match('/сехет$/u', $time) ? 'рен' : (preg_match('/ҫул/', $time) ? 'тан' : 'ран')); }, 'diff_yesterday' => 'Ӗнер', 'diff_today' => 'Паян', diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cy.php b/vendor/nesbot/carbon/src/Carbon/Lang/cy.php index 119274f01..d769d2f5f 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/cy.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/cy.php @@ -16,19 +16,26 @@ * - Daniel Monaghan */ return [ - 'year' => '{1}blwyddyn|]1,Inf[:count flynedd', + 'year' => '{1}:count flwyddyn|[-Inf,Inf]:count flynedd', + 'a_year' => '{1}blwyddyn|[-Inf,Inf]:count flynedd', 'y' => ':countbl', - 'month' => '{1}mis|]1,Inf[:count mis', + 'month' => ':count mis', + 'a_month' => '{1}mis|[-Inf,Inf]:count mis', 'm' => ':countmi', 'week' => ':count wythnos', + 'a_week' => '{1}wythnos|[-Inf,Inf]:count wythnos', 'w' => ':countw', - 'day' => '{1}diwrnod|]1,Inf[:count diwrnod', + 'day' => ':count diwrnod', + 'a_day' => '{1}diwrnod|[-Inf,Inf]:count diwrnod', 'd' => ':countd', - 'hour' => '{1}awr|]1,Inf[:count awr', + 'hour' => ':count awr', + 'a_hour' => '{1}awr|[-Inf,Inf]:count awr', 'h' => ':counth', - 'minute' => '{1}munud|]1,Inf[:count munud', + 'minute' => ':count munud', + 'a_minute' => '{1}munud|[-Inf,Inf]:count munud', 'min' => ':countm', - 'second' => '{1}ychydig eiliadau|]1,Inf[:count eiliad', + 'second' => ':count eiliad', + 'a_second' => '{0,1}ychydig eiliadau|[-Inf,Inf]:count eiliad', 's' => ':counts', 'ago' => ':time yn ôl', 'from_now' => 'mewn :time', @@ -57,7 +64,7 @@ 'lastWeek' => 'dddd [diwethaf am] LT', 'sameElse' => 'L', ], - 'ordinal' => function ($number) { + 'ordinal' => static function ($number) { return $number.( $number > 20 ? (\in_array((int) $number, [40, 50, 60, 80, 100], true) ? 'fed' : 'ain') diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/da.php b/vendor/nesbot/carbon/src/Carbon/Lang/da.php index 322f91d5b..d0e7168e5 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/da.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/da.php @@ -71,10 +71,10 @@ ], 'ordinal' => ':number.', 'months' => ['januar', 'februar', 'marts', 'april', 'maj', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'december'], - 'months_short' => ['jan.', 'feb.', 'mar.', 'apr.', 'maj.', 'jun.', 'jul.', 'aug.', 'sep.', 'okt.', 'nov.', 'dec.'], + 'months_short' => ['jan.', 'feb.', 'mar.', 'apr.', 'maj', 'jun.', 'jul.', 'aug.', 'sep.', 'okt.', 'nov.', 'dec.'], 'weekdays' => ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag', 'lørdag'], 'weekdays_short' => ['søn.', 'man.', 'tir.', 'ons.', 'tor.', 'fre.', 'lør.'], - 'weekdays_min' => ['sø', 'ma', 'ti', 'on', 'to', 'fr', 'lø'], + 'weekdays_min' => ['sø.', 'ma.', 'ti.', 'on.', 'to.', 'fr.', 'lø.'], 'first_day_of_week' => 1, 'day_of_first_week_of_year' => 4, 'list' => [', ', ' og '], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/dav.php b/vendor/nesbot/carbon/src/Carbon/Lang/dav.php index e95ec4bb2..4f8d1e73c 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/dav.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/dav.php @@ -10,6 +10,7 @@ */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'meridiem' => ['Luma lwa K', 'luma lwa p'], 'weekdays' => ['Ituku ja jumwa', 'Kuramuka jimweri', 'Kuramuka kawi', 'Kuramuka kadadu', 'Kuramuka kana', 'Kuramuka kasanu', 'Kifula nguwo'], 'weekdays_short' => ['Jum', 'Jim', 'Kaw', 'Kad', 'Kan', 'Kas', 'Ngu'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/de.php b/vendor/nesbot/carbon/src/Carbon/Lang/de.php index 3b70750e4..90b1e350c 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/de.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/de.php @@ -78,6 +78,24 @@ 'diff_before_yesterday' => 'Vorgestern', 'diff_after_tomorrow' => 'Übermorgen', + 'period_recurrences' => 'einmal|:count mal', + 'period_interval' => static function (string $interval = '') { + /** @var string $output */ + $output = preg_replace('/^(ein|eine|1)\s+/u', '', $interval); + + if (preg_match('/^(ein|1)( Monat| Mon.| Tag| Tg.)/u', $interval)) { + return "jeden $output"; + } + + if (preg_match('/^(ein|1)( Jahr| J.)/u', $interval)) { + return "jedes $output"; + } + + return "jede $output"; + }, + 'period_start_date' => 'von :date', + 'period_end_date' => 'bis :date', + 'formats' => [ 'LT' => 'HH:mm', 'LTS' => 'HH:mm:ss', diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/doi_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/doi_IN.php index d35972142..f3d43ce38 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/doi_IN.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/doi_IN.php @@ -22,6 +22,7 @@ 'weekdays' => ['ऐतबार', 'सोमबार', 'मंगलबर', 'बुधबार', 'बीरबार', 'शुक्करबार', 'श्नीचरबार'], 'weekdays_short' => ['ऐत', 'सोम', 'मंगल', 'बुध', 'बीर', 'शुक्कर', 'श्नीचर'], 'weekdays_min' => ['ऐत', 'सोम', 'मंगल', 'बुध', 'बीर', 'शुक्कर', 'श्नीचर'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['सञं', 'सबेर'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/dv.php b/vendor/nesbot/carbon/src/Carbon/Lang/dv.php index 4b8d7e1a4..b2062eb66 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/dv.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/dv.php @@ -10,18 +10,18 @@ */ $months = [ - 'ޖެނުއަރީ', - 'ފެބްރުއަރީ', + 'ޖަނަވަރީ', + 'ފެބުރުވަރީ', 'މާރިޗު', - 'އޭޕްރީލު', + 'އެޕްރީލް', 'މޭ', 'ޖޫން', 'ޖުލައި', - 'އޯގަސްޓު', - 'ސެޕްޓެމްބަރު', - 'އޮކްޓޯބަރު', - 'ނޮވެމްބަރު', - 'ޑިސެމްބަރު', + 'އޮގަސްޓު', + 'ސެޕްޓެންބަރު', + 'އޮކްޓޫބަރު', + 'ނޮވެންބަރު', + 'ޑިސެންބަރު', ]; $weekdays = [ @@ -38,6 +38,7 @@ * Authors: * - Josh Soref * - Jawish Hameed + * - Saiph Muhammad */ return [ 'year' => ':count '.'އަހަރު', diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/dz_BT.php b/vendor/nesbot/carbon/src/Carbon/Lang/dz_BT.php index bfbcaf468..5c40142d2 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/dz_BT.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/dz_BT.php @@ -22,6 +22,7 @@ 'weekdays' => ['གཟའ་ཟླ་བ་', 'གཟའ་མིག་དམར་', 'གཟའ་ལྷག་ཕ་', 'གཟའ་པུར་བུ་', 'གཟའ་པ་སངས་', 'གཟའ་སྤེན་ཕ་', 'གཟའ་ཉི་མ་'], 'weekdays_short' => ['ཟླ་', 'མིར་', 'ལྷག་', 'པུར་', 'སངས་', 'སྤེན་', 'ཉི་'], 'weekdays_min' => ['ཟླ་', 'མིར་', 'ལྷག་', 'པུར་', 'སངས་', 'སྤེན་', 'ཉི་'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['ངས་ཆ', 'ཕྱི་ཆ'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ebu.php b/vendor/nesbot/carbon/src/Carbon/Lang/ebu.php index f60bc6f2f..9e7d95771 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ebu.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ebu.php @@ -10,6 +10,7 @@ */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'meridiem' => ['KI', 'UT'], 'weekdays' => ['Kiumia', 'Njumatatu', 'Njumaine', 'Njumatano', 'Aramithi', 'Njumaa', 'NJumamothii'], 'weekdays_short' => ['Kma', 'Tat', 'Ine', 'Tan', 'Arm', 'Maa', 'NMM'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/el.php b/vendor/nesbot/carbon/src/Carbon/Lang/el.php index 7c40f9c12..8711fa2b4 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/el.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/el.php @@ -68,13 +68,9 @@ 'nextDay' => '[Αύριο {}] LT', 'nextWeek' => 'dddd [{}] LT', 'lastDay' => '[Χθες {}] LT', - 'lastWeek' => function (CarbonInterface $current) { - switch ($current->dayOfWeek) { - case 6: - return '[το προηγούμενο] dddd [{}] LT'; - default: - return '[την προηγούμενη] dddd [{}] LT'; - } + 'lastWeek' => static fn (CarbonInterface $current) => match ($current->dayOfWeek) { + 6 => '[το προηγούμενο] dddd [{}] LT', + default => '[την προηγούμενη] dddd [{}] LT', }, 'sameElse' => 'L', ], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en.php b/vendor/nesbot/carbon/src/Carbon/Lang/en.php index f81f617e3..79f50aad8 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en.php @@ -17,37 +17,37 @@ */ return [ /* - * {1}, {0} and ]1,Inf[ are not needed as it's the default for English pluralization. + * {1}, {0} and [-Inf,Inf] are not needed as it's the default for English pluralization. * But as some languages are using en.php as a fallback, it's better to specify it * explicitly so those languages also fallback to English pluralization when a unit * is missing. */ - 'year' => '{1}:count year|{0}:count years|]1,Inf[:count years', - 'a_year' => '{1}a year|{0}:count years|]1,Inf[:count years', - 'y' => '{1}:countyr|{0}:countyrs|]1,Inf[:countyrs', - 'month' => '{1}:count month|{0}:count months|]1,Inf[:count months', - 'a_month' => '{1}a month|{0}:count months|]1,Inf[:count months', - 'm' => '{1}:countmo|{0}:countmos|]1,Inf[:countmos', - 'week' => '{1}:count week|{0}:count weeks|]1,Inf[:count weeks', - 'a_week' => '{1}a week|{0}:count weeks|]1,Inf[:count weeks', + 'year' => '{1}:count year|{0}:count years|[-Inf,Inf]:count years', + 'a_year' => '{1}a year|{0}:count years|[-Inf,Inf]:count years', + 'y' => '{1}:countyr|{0}:countyrs|[-Inf,Inf]:countyrs', + 'month' => '{1}:count month|{0}:count months|[-Inf,Inf]:count months', + 'a_month' => '{1}a month|{0}:count months|[-Inf,Inf]:count months', + 'm' => '{1}:countmo|{0}:countmos|[-Inf,Inf]:countmos', + 'week' => '{1}:count week|{0}:count weeks|[-Inf,Inf]:count weeks', + 'a_week' => '{1}a week|{0}:count weeks|[-Inf,Inf]:count weeks', 'w' => ':countw', - 'day' => '{1}:count day|{0}:count days|]1,Inf[:count days', - 'a_day' => '{1}a day|{0}:count days|]1,Inf[:count days', + 'day' => '{1}:count day|{0}:count days|[-Inf,Inf]:count days', + 'a_day' => '{1}a day|{0}:count days|[-Inf,Inf]:count days', 'd' => ':countd', - 'hour' => '{1}:count hour|{0}:count hours|]1,Inf[:count hours', - 'a_hour' => '{1}an hour|{0}:count hours|]1,Inf[:count hours', + 'hour' => '{1}:count hour|{0}:count hours|[-Inf,Inf]:count hours', + 'a_hour' => '{1}an hour|{0}:count hours|[-Inf,Inf]:count hours', 'h' => ':counth', - 'minute' => '{1}:count minute|{0}:count minutes|]1,Inf[:count minutes', - 'a_minute' => '{1}a minute|{0}:count minutes|]1,Inf[:count minutes', + 'minute' => '{1}:count minute|{0}:count minutes|[-Inf,Inf]:count minutes', + 'a_minute' => '{1}a minute|{0}:count minutes|[-Inf,Inf]:count minutes', 'min' => ':countm', - 'second' => '{1}:count second|{0}:count seconds|]1,Inf[:count seconds', - 'a_second' => '{1}a few seconds|{0}:count seconds|]1,Inf[:count seconds', + 'second' => '{1}:count second|{0}:count seconds|[-Inf,Inf]:count seconds', + 'a_second' => '{0,1}a few seconds|[-Inf,Inf]:count seconds', 's' => ':counts', - 'millisecond' => '{1}:count millisecond|{0}:count milliseconds|]1,Inf[:count milliseconds', - 'a_millisecond' => '{1}a millisecond|{0}:count milliseconds|]1,Inf[:count milliseconds', + 'millisecond' => '{1}:count millisecond|{0}:count milliseconds|[-Inf,Inf]:count milliseconds', + 'a_millisecond' => '{1}a millisecond|{0}:count milliseconds|[-Inf,Inf]:count milliseconds', 'ms' => ':countms', - 'microsecond' => '{1}:count microsecond|{0}:count microseconds|]1,Inf[:count microseconds', - 'a_microsecond' => '{1}a microsecond|{0}:count microseconds|]1,Inf[:count microseconds', + 'microsecond' => '{1}:count microsecond|{0}:count microseconds|[-Inf,Inf]:count microseconds', + 'a_microsecond' => '{1}a microsecond|{0}:count microseconds|[-Inf,Inf]:count microseconds', 'µs' => ':countµs', 'ago' => ':time ago', 'from_now' => ':time from now', @@ -59,7 +59,7 @@ 'diff_tomorrow' => 'tomorrow', 'diff_before_yesterday' => 'before yesterday', 'diff_after_tomorrow' => 'after tomorrow', - 'period_recurrences' => '{1}once|{0}:count times|]1,Inf[:count times', + 'period_recurrences' => '{1}once|{0}:count times|[-Inf,Inf]:count times', 'period_interval' => 'every :interval', 'period_start_date' => 'from :date', 'period_end_date' => 'to :date', @@ -68,7 +68,7 @@ 'weekdays' => ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], 'weekdays_short' => ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], 'weekdays_min' => ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], - 'ordinal' => function ($number) { + 'ordinal' => static function ($number) { $lastDigit = $number % 10; return $number.( @@ -81,7 +81,15 @@ ) ); }, + 'formats' => [ + 'LT' => 'h:mm A', + 'LTS' => 'h:mm:ss A', + 'L' => 'MM/DD/YYYY', + 'LL' => 'MMMM D, YYYY', + 'LLL' => 'MMMM D, YYYY h:mm A', + 'LLLL' => 'dddd, MMMM D, YYYY h:mm A', + ], 'list' => [', ', ' and '], - 'first_day_of_week' => 0, + 'first_day_of_week' => 1, 'day_of_first_week_of_year' => 1, ]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_001.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_001.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_001.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_001.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_150.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_150.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_150.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_150.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_AG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_AG.php index 2c1c64f06..06c6e1ac1 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_AG.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_AG.php @@ -17,5 +17,4 @@ 'formats' => [ 'L' => 'DD/MM/YY', ], - 'day_of_first_week_of_year' => 1, ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_AI.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_AI.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_AI.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_AI.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_AS.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_AS.php index f086dc632..f4cdb67e9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_AS.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_AS.php @@ -9,4 +9,6 @@ * file that was distributed with this source code. */ -return require __DIR__.'/en.php'; +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_AT.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_AT.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_AT.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_AT.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_AU.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_AU.php index f16bd4f54..d1df6144c 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_AU.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_AU.php @@ -26,6 +26,5 @@ 'LLL' => 'D MMMM YYYY h:mm A', 'LLLL' => 'dddd, D MMMM YYYY h:mm A', ], - 'first_day_of_week' => 1, 'day_of_first_week_of_year' => 4, ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_BB.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_BB.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_BB.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_BB.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_BE.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_BE.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_BE.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_BE.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_BI.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_BI.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_BI.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_BI.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_BM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_BM.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_BM.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_BM.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_BS.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_BS.php index f086dc632..f4cdb67e9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_BS.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_BS.php @@ -9,4 +9,6 @@ * file that was distributed with this source code. */ -return require __DIR__.'/en.php'; +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_BW.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_BW.php index f086dc632..f4cdb67e9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_BW.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_BW.php @@ -9,4 +9,6 @@ * file that was distributed with this source code. */ -return require __DIR__.'/en.php'; +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_BZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_BZ.php index f086dc632..f4cdb67e9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_BZ.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_BZ.php @@ -9,4 +9,6 @@ * file that was distributed with this source code. */ -return require __DIR__.'/en.php'; +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_CA.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_CA.php index e65608688..824571e08 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_CA.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_CA.php @@ -26,4 +26,5 @@ 'LLL' => 'MMMM D, YYYY h:mm A', 'LLLL' => 'dddd, MMMM D, YYYY h:mm A', ], + 'first_day_of_week' => 0, ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_CC.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_CC.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_CC.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_CC.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_CH.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_CH.php index 10d9cd8f1..16668ffdf 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_CH.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_CH.php @@ -10,7 +10,6 @@ */ return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, 'formats' => [ 'LT' => 'HH:mm', 'LTS' => 'HH:mm:ss', diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_CK.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_CK.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_CK.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_CK.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_CM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_CM.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_CM.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_CM.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_CX.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_CX.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_CX.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_CX.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_CY.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_CY.php index a44c3508e..7dfb7210e 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_CY.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_CY.php @@ -23,6 +23,5 @@ 'LLL' => 'D MMMM YYYY HH:mm', 'LLLL' => 'dddd, D MMMM YYYY HH:mm', ], - 'first_day_of_week' => 1, 'day_of_first_week_of_year' => 4, ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_DE.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_DE.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_DE.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_DE.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_DG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_DG.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_DG.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_DG.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_DK.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_DK.php index 9e8a8c68b..9615e0438 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_DK.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_DK.php @@ -17,6 +17,5 @@ 'formats' => [ 'L' => 'YYYY-MM-DD', ], - 'first_day_of_week' => 1, 'day_of_first_week_of_year' => 4, ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_DM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_DM.php index f086dc632..f4cdb67e9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_DM.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_DM.php @@ -9,4 +9,6 @@ * file that was distributed with this source code. */ -return require __DIR__.'/en.php'; +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_ER.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_ER.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_ER.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_ER.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_FI.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_FI.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_FI.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_FI.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_FJ.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_FJ.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_FJ.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_FJ.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_FK.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_FK.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_FK.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_FK.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_FM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_FM.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_FM.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_FM.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_GB.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_GB.php index 67d9fd649..6ba710152 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_GB.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_GB.php @@ -25,6 +25,5 @@ 'LLL' => 'D MMMM YYYY HH:mm', 'LLLL' => 'dddd, D MMMM YYYY HH:mm', ], - 'first_day_of_week' => 1, 'day_of_first_week_of_year' => 4, ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_GD.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_GD.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_GD.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_GD.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_GG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_GG.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_GG.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_GG.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_GH.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_GH.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_GH.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_GH.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_GI.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_GI.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_GI.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_GI.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_GM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_GM.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_GM.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_GM.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_GU.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_GU.php index f086dc632..f4cdb67e9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_GU.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_GU.php @@ -9,4 +9,6 @@ * file that was distributed with this source code. */ -return require __DIR__.'/en.php'; +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_GY.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_GY.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_GY.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_GY.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_HK.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_HK.php index 34aae989b..d0963b4e1 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_HK.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_HK.php @@ -14,5 +14,5 @@ * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org */ return array_replace_recursive(require __DIR__.'/en.php', [ - 'day_of_first_week_of_year' => 1, + 'first_day_of_week' => 0, ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_IE.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_IE.php index c8d3c2fc9..62e5092f8 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_IE.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_IE.php @@ -26,6 +26,5 @@ 'LLL' => 'D MMMM YYYY HH:mm', 'LLLL' => 'dddd D MMMM YYYY HH:mm', ], - 'first_day_of_week' => 1, 'day_of_first_week_of_year' => 4, ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_IL.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_IL.php index e607924e7..b6107a96c 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_IL.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_IL.php @@ -26,4 +26,5 @@ 'LLL' => 'D MMMM YYYY HH:mm', 'LLLL' => 'dddd, D MMMM YYYY HH:mm', ], + 'first_day_of_week' => 0, ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_IM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_IM.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_IM.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_IM.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_IN.php index 00414e9ad..4a3f03146 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_IN.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_IN.php @@ -22,5 +22,4 @@ 'LLL' => 'DD MMM HH:mm', 'LLLL' => 'MMMM DD, YYYY HH:mm', ], - 'day_of_first_week_of_year' => 1, ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_IO.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_IO.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_IO.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_IO.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_ISO.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_ISO.php index 11457b0c8..6688b137f 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_ISO.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_ISO.php @@ -18,4 +18,5 @@ 'LLL' => 'YYYY MMMM D HH:mm', 'LLLL' => 'dddd, YYYY MMMM DD HH:mm', ], + 'first_day_of_week' => 0, ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_JE.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_JE.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_JE.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_JE.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_JM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_JM.php index f086dc632..f4cdb67e9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_JM.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_JM.php @@ -9,4 +9,6 @@ * file that was distributed with this source code. */ -return require __DIR__.'/en.php'; +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_KE.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_KE.php index f086dc632..f4cdb67e9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_KE.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_KE.php @@ -9,4 +9,6 @@ * file that was distributed with this source code. */ -return require __DIR__.'/en.php'; +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_KI.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_KI.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_KI.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_KI.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_KN.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_KN.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_KN.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_KN.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_KY.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_KY.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_KY.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_KY.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_LC.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_LC.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_LC.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_LC.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_LR.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_LR.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_LR.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_LR.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_LS.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_LS.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_LS.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_LS.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MG.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_MG.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MG.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MH.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MH.php index f086dc632..f4cdb67e9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_MH.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MH.php @@ -9,4 +9,6 @@ * file that was distributed with this source code. */ -return require __DIR__.'/en.php'; +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MO.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MO.php index f086dc632..f4cdb67e9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_MO.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MO.php @@ -9,4 +9,6 @@ * file that was distributed with this source code. */ -return require __DIR__.'/en.php'; +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MP.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MP.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_MP.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MP.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MS.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MS.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_MS.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MS.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MT.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MT.php index f086dc632..f4cdb67e9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_MT.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MT.php @@ -9,4 +9,6 @@ * file that was distributed with this source code. */ -return require __DIR__.'/en.php'; +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MU.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MU.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_MU.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MU.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MW.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MW.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_MW.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MW.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MY.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MY.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_MY.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MY.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_NA.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_NA.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_NA.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_NA.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_NF.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_NF.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_NF.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_NF.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_NG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_NG.php index 67bceaad7..c337cfc04 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_NG.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_NG.php @@ -13,6 +13,5 @@ 'formats' => [ 'L' => 'DD/MM/YY', ], - 'first_day_of_week' => 1, 'day_of_first_week_of_year' => 1, ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_NL.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_NL.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_NL.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_NL.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_NR.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_NR.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_NR.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_NR.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_NU.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_NU.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_NU.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_NU.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_NZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_NZ.php index 6a206a0d0..be65cd3e1 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_NZ.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_NZ.php @@ -26,6 +26,5 @@ 'LLL' => 'D MMMM YYYY h:mm A', 'LLLL' => 'dddd, D MMMM YYYY h:mm A', ], - 'first_day_of_week' => 1, 'day_of_first_week_of_year' => 4, ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_PG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_PG.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_PG.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_PG.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_PH.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_PH.php index 34aae989b..d0963b4e1 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_PH.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_PH.php @@ -14,5 +14,5 @@ * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org */ return array_replace_recursive(require __DIR__.'/en.php', [ - 'day_of_first_week_of_year' => 1, + 'first_day_of_week' => 0, ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_PK.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_PK.php index f086dc632..f4cdb67e9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_PK.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_PK.php @@ -9,4 +9,6 @@ * file that was distributed with this source code. */ -return require __DIR__.'/en.php'; +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_PN.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_PN.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_PN.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_PN.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_PR.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_PR.php index f086dc632..f4cdb67e9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_PR.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_PR.php @@ -9,4 +9,6 @@ * file that was distributed with this source code. */ -return require __DIR__.'/en.php'; +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_PW.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_PW.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_PW.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_PW.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_RW.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_RW.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_RW.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_RW.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SB.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SB.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_SB.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SB.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SC.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SC.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_SC.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SC.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SE.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SE.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_SE.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SE.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SG.php index 5ee95241d..e31e8264f 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_SG.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SG.php @@ -19,6 +19,5 @@ 'LLL' => 'D MMMM YYYY HH:mm', 'LLLL' => 'dddd, D MMMM YYYY HH:mm', ], - 'first_day_of_week' => 1, 'day_of_first_week_of_year' => 4, ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SH.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SH.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_SH.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SH.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SI.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SI.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_SI.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SI.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SL.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SL.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_SL.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SL.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SS.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SS.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_SS.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SS.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SX.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SX.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_SX.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SX.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SZ.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_SZ.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SZ.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_TC.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_TC.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_TC.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_TC.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_TK.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_TK.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_TK.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_TK.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_TO.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_TO.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_TO.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_TO.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_TT.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_TT.php index f086dc632..f4cdb67e9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_TT.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_TT.php @@ -9,4 +9,6 @@ * file that was distributed with this source code. */ -return require __DIR__.'/en.php'; +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_TV.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_TV.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_TV.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_TV.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_TZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_TZ.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_TZ.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_TZ.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_UG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_UG.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_UG.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_UG.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_UM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_UM.php index f086dc632..f4cdb67e9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_UM.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_UM.php @@ -9,4 +9,6 @@ * file that was distributed with this source code. */ -return require __DIR__.'/en.php'; +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_US.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_US.php index f086dc632..f4cdb67e9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_US.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_US.php @@ -9,4 +9,6 @@ * file that was distributed with this source code. */ -return require __DIR__.'/en.php'; +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_US_Posix.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_US_Posix.php index f086dc632..135bc0c36 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_US_Posix.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_US_Posix.php @@ -9,4 +9,4 @@ * file that was distributed with this source code. */ -return require __DIR__.'/en.php'; +return require __DIR__.'/en_US.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_VC.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_VC.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_VC.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_VC.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_VG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_VG.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_VG.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_VG.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_VI.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_VI.php index f086dc632..f4cdb67e9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_VI.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_VI.php @@ -9,4 +9,6 @@ * file that was distributed with this source code. */ -return require __DIR__.'/en.php'; +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_VU.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_VU.php index e2dd81db5..f086dc632 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_VU.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_VU.php @@ -9,6 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/en.php', [ - 'first_day_of_week' => 1, -]); +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_WS.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_WS.php index f086dc632..f4cdb67e9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_WS.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_WS.php @@ -9,4 +9,6 @@ * file that was distributed with this source code. */ -return require __DIR__.'/en.php'; +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_ZA.php index 48ea9471c..54d8d8818 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_ZA.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_ZA.php @@ -22,5 +22,4 @@ 'LLL' => 'DD MMM HH:mm', 'LLLL' => 'MMMM DD, YYYY HH:mm', ], - 'day_of_first_week_of_year' => 1, ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_ZM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_ZM.php index d8a8cb598..c6bc0b2ff 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_ZM.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_ZM.php @@ -17,6 +17,4 @@ 'formats' => [ 'L' => 'DD/MM/YY', ], - 'first_day_of_week' => 1, - 'day_of_first_week_of_year' => 1, ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_ZW.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_ZW.php index f086dc632..f4cdb67e9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/en_ZW.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_ZW.php @@ -9,4 +9,6 @@ * file that was distributed with this source code. */ -return require __DIR__.'/en.php'; +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es.php b/vendor/nesbot/carbon/src/Carbon/Lang/es.php index 1c4fcfd0b..dc24945cf 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/es.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es.php @@ -72,6 +72,10 @@ 'diff_tomorrow_regexp' => 'mañana(?:\\s+a)?(?:\\s+las)?', 'diff_before_yesterday' => 'anteayer', 'diff_after_tomorrow' => 'pasado mañana', + 'period_recurrences' => 'una vez|:count veces', + 'period_interval' => 'cada :interval', + 'period_start_date' => 'de :date', + 'period_end_date' => 'a :date', 'formats' => [ 'LT' => 'H:mm', 'LTS' => 'H:mm:ss', @@ -81,19 +85,19 @@ 'LLLL' => 'dddd, D [de] MMMM [de] YYYY H:mm', ], 'calendar' => [ - 'sameDay' => function (CarbonInterface $current) { + 'sameDay' => static function (CarbonInterface $current) { return '[hoy a la'.($current->hour !== 1 ? 's' : '').'] LT'; }, - 'nextDay' => function (CarbonInterface $current) { + 'nextDay' => static function (CarbonInterface $current) { return '[mañana a la'.($current->hour !== 1 ? 's' : '').'] LT'; }, - 'nextWeek' => function (CarbonInterface $current) { + 'nextWeek' => static function (CarbonInterface $current) { return 'dddd [a la'.($current->hour !== 1 ? 's' : '').'] LT'; }, - 'lastDay' => function (CarbonInterface $current) { + 'lastDay' => static function (CarbonInterface $current) { return '[ayer a la'.($current->hour !== 1 ? 's' : '').'] LT'; }, - 'lastWeek' => function (CarbonInterface $current) { + 'lastWeek' => static function (CarbonInterface $current) { return '[el] dddd [pasado a la'.($current->hour !== 1 ? 's' : '').'] LT'; }, 'sameElse' => 'L', diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fil_PH.php b/vendor/nesbot/carbon/src/Carbon/Lang/fil_PH.php index bcf158078..60751dd53 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/fil_PH.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fil_PH.php @@ -23,6 +23,7 @@ 'weekdays' => ['Linggo', 'Lunes', 'Martes', 'Miyerkoles', 'Huwebes', 'Biyernes', 'Sabado'], 'weekdays_short' => ['Lin', 'Lun', 'Mar', 'Miy', 'Huw', 'Biy', 'Sab'], 'weekdays_min' => ['Lin', 'Lun', 'Mar', 'Miy', 'Huw', 'Biy', 'Sab'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['N.U.', 'N.H.'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr.php index f4c7247b4..da696e79e 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/fr.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr.php @@ -23,6 +23,12 @@ * - Pete Scopes (pdscopes) */ return [ + 'millennium' => ':count millénaire|:count millénaires', + 'a_millennium' => 'un millénaire|:count millénaires', + 'century' => ':count siècle|:count siècles', + 'a_century' => 'un siècle|:count siècles', + 'decade' => ':count décennie|:count décennies', + 'a_decade' => 'une décennie|:count décennies', 'year' => ':count an|:count ans', 'a_year' => 'un an|:count ans', 'y' => ':count an|:count ans', @@ -88,25 +94,14 @@ 'weekdays' => ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'], 'weekdays_short' => ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'], 'weekdays_min' => ['di', 'lu', 'ma', 'me', 'je', 've', 'sa'], - 'ordinal' => function ($number, $period) { - switch ($period) { + 'ordinal' => static function ($number, $period) { + return match ($period) { // In French, only the first has to be ordinal, other number remains cardinal // @link https://fr.wikihow.com/%C3%A9crire-la-date-en-fran%C3%A7ais - case 'D': - return $number.($number === 1 ? 'er' : ''); - - default: - case 'M': - case 'Q': - case 'DDD': - case 'd': - return $number.($number === 1 ? 'er' : 'e'); - - // Words with feminine grammatical gender: semaine - case 'w': - case 'W': - return $number.($number === 1 ? 're' : 'e'); - } + 'D' => $number.($number === 1 ? 'er' : ''), + default => $number.($number === 1 ? 'er' : 'e'), + 'w', 'W' => $number.($number === 1 ? 're' : 'e'), + }; }, 'first_day_of_week' => 1, 'day_of_first_week_of_year' => 4, diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_BE.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_BE.php index f6cafe87c..8ed032847 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/fr_BE.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_BE.php @@ -13,6 +13,4 @@ * Authors: * - RAP bug-glibc-locales@gnu.org */ -return array_replace_recursive(require __DIR__.'/fr.php', [ - 'months_short' => ['jan', 'fév', 'mar', 'avr', 'mai', 'jun', 'jui', 'aoû', 'sep', 'oct', 'nov', 'déc'], -]); +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_LU.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_LU.php index 8e37d852b..6dda77226 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/fr_LU.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_LU.php @@ -17,5 +17,4 @@ 'formats' => [ 'L' => 'DD.MM.YYYY', ], - 'months_short' => ['jan', 'fév', 'mar', 'avr', 'mai', 'jun', 'jui', 'aoû', 'sep', 'oct', 'nov', 'déc'], ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fy.php b/vendor/nesbot/carbon/src/Carbon/Lang/fy.php index c1b543970..42ecaed1e 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/fy.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fy.php @@ -61,7 +61,7 @@ 'lastWeek' => '[ôfrûne] dddd [om] LT', 'sameElse' => 'L', ], - 'ordinal' => function ($number) { + 'ordinal' => static function ($number) { return $number.(($number === 1 || $number === 8 || $number >= 20) ? 'ste' : 'de'); }, 'months' => ['jannewaris', 'febrewaris', 'maart', 'april', 'maaie', 'juny', 'july', 'augustus', 'septimber', 'oktober', 'novimber', 'desimber'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ga.php b/vendor/nesbot/carbon/src/Carbon/Lang/ga.php index 9f07a26ce..013367bd8 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ga.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ga.php @@ -67,9 +67,7 @@ 'weekdays' => ['Dé Domhnaigh', 'Dé Luain', 'Dé Máirt', 'Dé Céadaoin', 'Déardaoin', 'Dé hAoine', 'Dé Satharn'], 'weekdays_short' => ['Dom', 'Lua', 'Mái', 'Céa', 'Déa', 'hAo', 'Sat'], 'weekdays_min' => ['Do', 'Lu', 'Má', 'Ce', 'Dé', 'hA', 'Sa'], - 'ordinal' => function ($number) { - return $number.($number === 1 ? 'd' : ($number % 10 === 2 ? 'na' : 'mh')); - }, + 'ordinal' => static fn ($number) => $number.($number === 1 ? 'd' : ($number % 10 === 2 ? 'na' : 'mh')), 'first_day_of_week' => 1, 'day_of_first_week_of_year' => 4, 'list' => [', ', ' agus '], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gd.php b/vendor/nesbot/carbon/src/Carbon/Lang/gd.php index 63d064ddd..05437bff5 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/gd.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/gd.php @@ -60,7 +60,7 @@ 'lastWeek' => 'dddd [seo chaidh] [aig] LT', 'sameElse' => 'L', ], - 'ordinal' => function ($number) { + 'ordinal' => static function ($number) { return $number.($number === 1 ? 'd' : ($number % 10 === 2 ? 'na' : 'mh')); }, 'months' => ['Am Faoilleach', 'An Gearran', 'Am Màrt', 'An Giblean', 'An Cèitean', 'An t-Ògmhios', 'An t-Iuchar', 'An Lùnastal', 'An t-Sultain', 'An Dàmhair', 'An t-Samhain', 'An Dùbhlachd'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gez_ET.php b/vendor/nesbot/carbon/src/Carbon/Lang/gez_ET.php index 393300962..857954981 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/gez_ET.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/gez_ET.php @@ -22,6 +22,7 @@ 'weekdays' => ['እኁድ', 'ሰኑይ', 'ሠሉስ', 'ራብዕ', 'ሐሙስ', 'ዓርበ', 'ቀዳሚት'], 'weekdays_short' => ['እኁድ', 'ሰኑይ', 'ሠሉስ', 'ራብዕ', 'ሐሙስ', 'ዓርበ', 'ቀዳሚ'], 'weekdays_min' => ['እኁድ', 'ሰኑይ', 'ሠሉስ', 'ራብዕ', 'ሐሙስ', 'ዓርበ', 'ቀዳሚ'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['ጽባሕ', 'ምሴት'], ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gl.php b/vendor/nesbot/carbon/src/Carbon/Lang/gl.php index 088b0f281..96bf14561 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/gl.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/gl.php @@ -43,7 +43,7 @@ 'a_second' => 'uns segundos|:count segundos', 's' => ':count seg.', 'ago' => 'hai :time', - 'from_now' => function ($time) { + 'from_now' => static function ($time) { if (str_starts_with($time, 'un')) { return "n$time"; } @@ -68,19 +68,19 @@ 'LLLL' => 'dddd, D [de] MMMM [de] YYYY H:mm', ], 'calendar' => [ - 'sameDay' => function (CarbonInterface $current) { + 'sameDay' => static function (CarbonInterface $current) { return '[hoxe '.($current->hour !== 1 ? 'ás' : 'á').'] LT'; }, - 'nextDay' => function (CarbonInterface $current) { + 'nextDay' => static function (CarbonInterface $current) { return '[mañá '.($current->hour !== 1 ? 'ás' : 'á').'] LT'; }, - 'nextWeek' => function (CarbonInterface $current) { + 'nextWeek' => static function (CarbonInterface $current) { return 'dddd ['.($current->hour !== 1 ? 'ás' : 'á').'] LT'; }, - 'lastDay' => function (CarbonInterface $current) { + 'lastDay' => static function (CarbonInterface $current) { return '[onte '.($current->hour !== 1 ? 'á' : 'a').'] LT'; }, - 'lastWeek' => function (CarbonInterface $current) { + 'lastWeek' => static function (CarbonInterface $current) { return '[o] dddd [pasado '.($current->hour !== 1 ? 'ás' : 'á').'] LT'; }, 'sameElse' => 'L', diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gom_Latn.php b/vendor/nesbot/carbon/src/Carbon/Lang/gom_Latn.php index 612bb8865..ef50d96e2 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/gom_Latn.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/gom_Latn.php @@ -53,11 +53,9 @@ 'weekdays_short' => ['Ait.', 'Som.', 'Mon.', 'Bud.', 'Bre.', 'Suk.', 'Son.'], 'weekdays_min' => ['Ai', 'Sm', 'Mo', 'Bu', 'Br', 'Su', 'Sn'], - 'ordinal' => function ($number, $period) { - return $number.($period === 'D' ? 'er' : ''); - }, + 'ordinal' => static fn ($number, $period) => $number.($period === 'D' ? 'er' : ''), - 'meridiem' => function ($hour) { + 'meridiem' => static function ($hour) { if ($hour < 4) { return 'rati'; } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gu.php b/vendor/nesbot/carbon/src/Carbon/Lang/gu.php index 8bc431140..16455eded 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/gu.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/gu.php @@ -54,7 +54,7 @@ 'lastWeek' => '[પાછલા] dddd, LT', 'sameElse' => 'L', ], - 'meridiem' => function ($hour) { + 'meridiem' => static function ($hour) { if ($hour < 4) { return 'રાત'; } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/guz.php b/vendor/nesbot/carbon/src/Carbon/Lang/guz.php index 6230165c1..d39e9ca3b 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/guz.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/guz.php @@ -10,6 +10,7 @@ */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'meridiem' => ['Ma', 'Mo'], 'weekdays' => ['Chumapiri', 'Chumatato', 'Chumaine', 'Chumatano', 'Aramisi', 'Ichuma', 'Esabato'], 'weekdays_short' => ['Cpr', 'Ctt', 'Cmn', 'Cmt', 'Ars', 'Icm', 'Est'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hak_TW.php b/vendor/nesbot/carbon/src/Carbon/Lang/hak_TW.php index fe2398650..2a3bc9614 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/hak_TW.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hak_TW.php @@ -22,6 +22,7 @@ 'weekdays' => ['禮拜日', '禮拜一', '禮拜二', '禮拜三', '禮拜四', '禮拜五', '禮拜六'], 'weekdays_short' => ['日', '一', '二', '三', '四', '五', '六'], 'weekdays_min' => ['日', '一', '二', '三', '四', '五', '六'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['上晝', '下晝'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/haw.php b/vendor/nesbot/carbon/src/Carbon/Lang/haw.php index cdd36861e..e46993a3c 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/haw.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/haw.php @@ -10,6 +10,7 @@ */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'months' => ['Ianuali', 'Pepeluali', 'Malaki', 'ʻApelila', 'Mei', 'Iune', 'Iulai', 'ʻAukake', 'Kepakemapa', 'ʻOkakopa', 'Nowemapa', 'Kekemapa'], 'months_short' => ['Ian.', 'Pep.', 'Mal.', 'ʻAp.', 'Mei', 'Iun.', 'Iul.', 'ʻAu.', 'Kep.', 'ʻOk.', 'Now.', 'Kek.'], 'weekdays' => ['Lāpule', 'Poʻakahi', 'Poʻalua', 'Poʻakolu', 'Poʻahā', 'Poʻalima', 'Poʻaono'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/he.php b/vendor/nesbot/carbon/src/Carbon/Lang/he.php index c3fb3e976..6d8f01ee6 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/he.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/he.php @@ -60,7 +60,7 @@ 'lastWeek' => '[ביום] dddd [האחרון בשעה] LT', 'sameElse' => 'L', ], - 'meridiem' => function ($hour, $minute, $isLower) { + 'meridiem' => static function ($hour, $minute, $isLower) { if ($hour < 5) { return 'לפנות בוקר'; } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hi.php b/vendor/nesbot/carbon/src/Carbon/Lang/hi.php index 70c57a297..1fc180102 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/hi.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hi.php @@ -54,7 +54,7 @@ 'lastWeek' => '[पिछले] dddd, LT', 'sameElse' => 'L', ], - 'meridiem' => function ($hour) { + 'meridiem' => static function ($hour) { if ($hour < 4) { return 'रात'; } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hif_FJ.php b/vendor/nesbot/carbon/src/Carbon/Lang/hif_FJ.php index 30ad5e74c..54e880e36 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/hif_FJ.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hif_FJ.php @@ -14,6 +14,7 @@ * - Samsung Electronics Co., Ltd. akhilesh.k@samsung.com */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'formats' => [ 'L' => 'dddd DD MMM YYYY', ], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hne_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/hne_IN.php index a5ca758b6..27b3b396a 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/hne_IN.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hne_IN.php @@ -22,6 +22,7 @@ 'weekdays' => ['इतवार', 'सोमवार', 'मंगलवार', 'बुधवार', 'बिरसपत', 'सुकरवार', 'सनिवार'], 'weekdays_short' => ['इत', 'सोम', 'मंग', 'बुध', 'बिर', 'सुक', 'सनि'], 'weekdays_min' => ['इत', 'सोम', 'मंग', 'बुध', 'बिर', 'सुक', 'सनि'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['बिहिनियाँ', 'मंझनियाँ'], ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hr.php b/vendor/nesbot/carbon/src/Carbon/Lang/hr.php index cfd85fd40..dcf775659 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/hr.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hr.php @@ -70,29 +70,17 @@ 'calendar' => [ 'sameDay' => '[danas u] LT', 'nextDay' => '[sutra u] LT', - 'nextWeek' => function (CarbonInterface $date) { - switch ($date->dayOfWeek) { - case 0: - return '[u] [nedjelju] [u] LT'; - case 3: - return '[u] [srijedu] [u] LT'; - case 6: - return '[u] [subotu] [u] LT'; - default: - return '[u] dddd [u] LT'; - } + 'nextWeek' => static fn (CarbonInterface $date) => match ($date->dayOfWeek) { + 0 => '[u] [nedjelju] [u] LT', + 3 => '[u] [srijedu] [u] LT', + 6 => '[u] [subotu] [u] LT', + default => '[u] dddd [u] LT', }, 'lastDay' => '[jučer u] LT', - 'lastWeek' => function (CarbonInterface $date) { - switch ($date->dayOfWeek) { - case 0: - case 3: - return '[prošlu] dddd [u] LT'; - case 6: - return '[prošle] [subote] [u] LT'; - default: - return '[prošli] dddd [u] LT'; - } + 'lastWeek' => static fn (CarbonInterface $date) => match ($date->dayOfWeek) { + 0, 3 => '[prošlu] dddd [u] LT', + 6 => '[prošle] [subote] [u] LT', + default => '[prošli] dddd [u] LT', }, 'sameElse' => 'L', ], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hu.php b/vendor/nesbot/carbon/src/Carbon/Lang/hu.php index b7583eecd..635a30cf9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/hu.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hu.php @@ -102,11 +102,11 @@ 'calendar' => [ 'sameDay' => '[ma] LT[-kor]', 'nextDay' => '[holnap] LT[-kor]', - 'nextWeek' => function (CarbonInterface $date) use ($huWeekEndings) { + 'nextWeek' => static function (CarbonInterface $date) use ($huWeekEndings) { return '['.$huWeekEndings[$date->dayOfWeek].'] LT[-kor]'; }, 'lastDay' => '[tegnap] LT[-kor]', - 'lastWeek' => function (CarbonInterface $date) use ($huWeekEndings) { + 'lastWeek' => static function (CarbonInterface $date) use ($huWeekEndings) { return '[múlt '.$huWeekEndings[$date->dayOfWeek].'] LT[-kor]'; }, 'sameElse' => 'L', diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hy.php b/vendor/nesbot/carbon/src/Carbon/Lang/hy.php index 8b129947d..8145ba31e 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/hy.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hy.php @@ -59,18 +59,13 @@ 'lastWeek' => '[անցած] dddd [օրը ժամը] LT', 'sameElse' => 'L', ], - 'ordinal' => function ($number, $period) { - switch ($period) { - case 'DDD': - case 'w': - case 'W': - case 'DDDo': - return $number.($number === 1 ? '-ին' : '-րդ'); - default: - return $number; - } + 'ordinal' => static function ($number, $period) { + return match ($period) { + 'DDD', 'w', 'W', 'DDDo' => $number.($number === 1 ? '-ին' : '-րդ'), + default => $number, + }; }, - 'meridiem' => function ($hour) { + 'meridiem' => static function ($hour) { if ($hour < 4) { return 'գիշերվա'; } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/id.php b/vendor/nesbot/carbon/src/Carbon/Lang/id.php index afaf78f29..a49a29327 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/id.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/id.php @@ -21,25 +21,25 @@ */ return [ 'year' => ':count tahun', - 'a_year' => '{1}setahun|]1,Inf[:count tahun', + 'a_year' => '{1}setahun|[-Inf,Inf]:count tahun', 'y' => ':countthn', 'month' => ':count bulan', - 'a_month' => '{1}sebulan|]1,Inf[:count bulan', + 'a_month' => '{1}sebulan|[-Inf,Inf]:count bulan', 'm' => ':countbln', 'week' => ':count minggu', - 'a_week' => '{1}seminggu|]1,Inf[:count minggu', + 'a_week' => '{1}seminggu|[-Inf,Inf]:count minggu', 'w' => ':countmgg', 'day' => ':count hari', - 'a_day' => '{1}sehari|]1,Inf[:count hari', + 'a_day' => '{1}sehari|[-Inf,Inf]:count hari', 'd' => ':counthr', 'hour' => ':count jam', - 'a_hour' => '{1}sejam|]1,Inf[:count jam', + 'a_hour' => '{1}sejam|[-Inf,Inf]:count jam', 'h' => ':countj', 'minute' => ':count menit', - 'a_minute' => '{1}semenit|]1,Inf[:count menit', + 'a_minute' => '{1}semenit|[-Inf,Inf]:count menit', 'min' => ':countmnt', 'second' => ':count detik', - 'a_second' => '{1}beberapa detik|]1,Inf[:count detik', + 'a_second' => '{1}beberapa detik|[-Inf,Inf]:count detik', 's' => ':countdt', 'ago' => ':time yang lalu', 'from_now' => ':time dari sekarang', @@ -68,7 +68,7 @@ 'lastWeek' => 'dddd [lalu pukul] LT', 'sameElse' => 'L', ], - 'meridiem' => function ($hour) { + 'meridiem' => static function ($hour) { if ($hour < 11) { return 'pagi'; } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ii.php b/vendor/nesbot/carbon/src/Carbon/Lang/ii.php index a4246c277..592a53f41 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ii.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ii.php @@ -10,6 +10,7 @@ */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'meridiem' => ['ꎸꄑ', 'ꁯꋒ'], 'weekdays' => ['ꑭꆏꑍ', 'ꆏꊂꋍ', 'ꆏꊂꑍ', 'ꆏꊂꌕ', 'ꆏꊂꇖ', 'ꆏꊂꉬ', 'ꆏꊂꃘ'], 'weekdays_short' => ['ꑭꆏ', 'ꆏꋍ', 'ꆏꑍ', 'ꆏꌕ', 'ꆏꇖ', 'ꆏꉬ', 'ꆏꃘ'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ik_CA.php b/vendor/nesbot/carbon/src/Carbon/Lang/ik_CA.php index bb2a109b9..02dbbef22 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ik_CA.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ik_CA.php @@ -22,6 +22,7 @@ 'weekdays' => ['Minġuiqsioiq', 'Savałłiq', 'Ilaqtchiioiq', 'Qitchiioiq', 'Sisamiioiq', 'Tallimmiioiq', 'Maqinġuoiq'], 'weekdays_short' => ['Min', 'Sav', 'Ila', 'Qit', 'Sis', 'Tal', 'Maq'], 'weekdays_min' => ['Min', 'Sav', 'Ila', 'Qit', 'Sis', 'Tal', 'Maq'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'year' => ':count ukiuq', diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/it.php b/vendor/nesbot/carbon/src/Carbon/Lang/it.php index 49875d7ef..c48369517 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/it.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/it.php @@ -54,7 +54,7 @@ 'a_microsecond' => 'un microsecondo|:count microsecondi', 'µs' => ':countµs', 'ago' => ':time fa', - 'from_now' => function ($time) { + 'from_now' => static function ($time) { return (preg_match('/^\d.+$/', $time) ? 'tra' : 'in')." $time"; }, 'after' => ':time dopo', @@ -84,13 +84,9 @@ 'nextDay' => '[Domani alle] LT', 'nextWeek' => 'dddd [alle] LT', 'lastDay' => '[Ieri alle] LT', - 'lastWeek' => function (CarbonInterface $date) { - switch ($date->dayOfWeek) { - case 0: - return '[la scorsa] dddd [alle] LT'; - default: - return '[lo scorso] dddd [alle] LT'; - } + 'lastWeek' => static fn (CarbonInterface $date) => match ($date->dayOfWeek) { + 0 => '[la scorsa] dddd [alle] LT', + default => '[lo scorso] dddd [alle] LT', }, 'sameElse' => 'L', ], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/iu_CA.php b/vendor/nesbot/carbon/src/Carbon/Lang/iu_CA.php index 6ab7e1497..5acee9390 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/iu_CA.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/iu_CA.php @@ -22,6 +22,7 @@ 'weekdays' => ['ᓈᑦᑎᖑᔭᕐᕕᒃ', 'ᓇᒡᒐᔾᔭᐅ', 'ᓇᒡᒐᔾᔭᐅᓕᖅᑭᑦ', 'ᐱᖓᓲᓕᖅᓯᐅᑦ', 'ᕿᑎᖅᑰᑦ', 'ᐅᓪᓗᕈᓘᑐᐃᓇᖅ', 'ᓯᕙᑖᕕᒃ'], 'weekdays_short' => ['ᓈ', 'ᓇ', 'ᓕ', 'ᐱ', 'ᕿ', 'ᐅ', 'ᓯ'], 'weekdays_min' => ['ᓈ', 'ᓇ', 'ᓕ', 'ᐱ', 'ᕿ', 'ᐅ', 'ᓯ'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'year' => ':count ᐅᑭᐅᖅ', diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/iw.php b/vendor/nesbot/carbon/src/Carbon/Lang/iw.php index a26e35062..5dedd3c02 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/iw.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/iw.php @@ -10,6 +10,7 @@ */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'months' => ['ינואר', 'פברואר', 'מרץ', 'אפריל', 'מאי', 'יוני', 'יולי', 'אוגוסט', 'ספטמבר', 'אוקטובר', 'נובמבר', 'דצמבר'], 'months_short' => ['ינו׳', 'פבר׳', 'מרץ', 'אפר׳', 'מאי', 'יוני', 'יולי', 'אוג׳', 'ספט׳', 'אוק׳', 'נוב׳', 'דצמ׳'], 'weekdays' => ['יום ראשון', 'יום שני', 'יום שלישי', 'יום רביעי', 'יום חמישי', 'יום שישי', 'יום שבת'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ja.php b/vendor/nesbot/carbon/src/Carbon/Lang/ja.php index 1ca675195..a857db5cc 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ja.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ja.php @@ -38,7 +38,7 @@ 'minute' => ':count分', 'min' => ':count分', 'second' => ':count秒', - 'a_second' => '{1}数秒|]1,Inf[:count秒', + 'a_second' => '{1}数秒|[-Inf,Inf]:count秒', 's' => ':count秒', 'ago' => ':time前', 'from_now' => ':time後', @@ -59,7 +59,7 @@ 'calendar' => [ 'sameDay' => '[今日] LT', 'nextDay' => '[明日] LT', - 'nextWeek' => function (CarbonInterface $current, CarbonInterface $other) { + 'nextWeek' => static function (CarbonInterface $current, \Carbon\CarbonInterface $other) { if ($other->week !== $current->week) { return '[来週]dddd LT'; } @@ -67,7 +67,7 @@ return 'dddd LT'; }, 'lastDay' => '[昨日] LT', - 'lastWeek' => function (CarbonInterface $current, CarbonInterface $other) { + 'lastWeek' => static function (CarbonInterface $current, \Carbon\CarbonInterface $other) { if ($other->week !== $current->week) { return '[先週]dddd LT'; } @@ -76,15 +76,11 @@ }, 'sameElse' => 'L', ], - 'ordinal' => function ($number, $period) { - switch ($period) { - case 'd': - case 'D': - case 'DDD': - return $number.'日'; - default: - return $number; - } + 'ordinal' => static function ($number, $period) { + return match ($period) { + 'd', 'D', 'DDD' => $number.'日', + default => $number, + }; }, 'meridiem' => ['午前', '午後'], 'months' => ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/jgo.php b/vendor/nesbot/carbon/src/Carbon/Lang/jgo.php index 6a1e77a85..f4cdb67e9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/jgo.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/jgo.php @@ -10,4 +10,5 @@ */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/jv.php b/vendor/nesbot/carbon/src/Carbon/Lang/jv.php index bcbe044e3..10fe5383c 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/jv.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/jv.php @@ -16,13 +16,20 @@ * - JD Isaacks */ return [ - 'year' => '{1}setaun|]1,Inf[:count taun', - 'month' => '{1}sewulan|]1,Inf[:count wulan', - 'week' => '{1}sakminggu|]1,Inf[:count minggu', - 'day' => '{1}sedinten|]1,Inf[:count dinten', - 'hour' => '{1}setunggal jam|]1,Inf[:count jam', - 'minute' => '{1}setunggal menit|]1,Inf[:count menit', - 'second' => '{1}sawetawis detik|]1,Inf[:count detik', + 'year' => ':count taun', + 'a_year' => '{1}setaun|[-Inf,Inf]:count taun', + 'month' => ':count wulan', + 'a_month' => '{1}sewulan|[-Inf,Inf]:count wulan', + 'week' => ':count minggu', + 'a_week' => '{1}sakminggu|[-Inf,Inf]:count minggu', + 'day' => ':count dina', + 'a_day' => '{1}sedina|[-Inf,Inf]:count dina', + 'hour' => ':count jam', + 'a_hour' => '{1}setunggal jam|[-Inf,Inf]:count jam', + 'minute' => ':count menit', + 'a_minute' => '{1}setunggal menit|[-Inf,Inf]:count menit', + 'second' => ':count detik', + 'a_second' => '{0,1}sawetawis detik|[-Inf,Inf]:count detik', 'ago' => ':time ingkang kepengker', 'from_now' => 'wonten ing :time', 'diff_today' => 'Dinten', @@ -47,7 +54,7 @@ 'lastWeek' => 'dddd [kepengker pukul] LT', 'sameElse' => 'L', ], - 'meridiem' => function ($hour) { + 'meridiem' => static function ($hour) { if ($hour < 11) { return 'enjing'; } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ka.php b/vendor/nesbot/carbon/src/Carbon/Lang/ka.php index a5d563d3c..b3051d598 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ka.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ka.php @@ -30,26 +30,26 @@ return [ 'year' => ':count წელი', 'y' => ':count წელი', - 'a_year' => '{1}წელი|]1,Inf[:count წელი', + 'a_year' => '{1}წელი|[-Inf,Inf]:count წელი', 'month' => ':count თვე', 'm' => ':count თვე', - 'a_month' => '{1}თვე|]1,Inf[:count თვე', + 'a_month' => '{1}თვე|[-Inf,Inf]:count თვე', 'week' => ':count კვირა', 'w' => ':count კვირა', - 'a_week' => '{1}კვირა|]1,Inf[:count კვირა', + 'a_week' => '{1}კვირა|[-Inf,Inf]:count კვირა', 'day' => ':count დღე', 'd' => ':count დღე', - 'a_day' => '{1}დღე|]1,Inf[:count დღე', + 'a_day' => '{1}დღე|[-Inf,Inf]:count დღე', 'hour' => ':count საათი', 'h' => ':count საათი', - 'a_hour' => '{1}საათი|]1,Inf[:count საათი', + 'a_hour' => '{1}საათი|[-Inf,Inf]:count საათი', 'minute' => ':count წუთი', 'min' => ':count წუთი', - 'a_minute' => '{1}წუთი|]1,Inf[:count წუთი', + 'a_minute' => '{1}წუთი|[-Inf,Inf]:count წუთი', 'second' => ':count წამი', 's' => ':count წამი', - 'a_second' => '{1}რამდენიმე წამი|]1,Inf[:count წამი', - 'ago' => function ($time) { + 'a_second' => '{1}რამდენიმე წამი|[-Inf,Inf]:count წამი', + 'ago' => static function ($time) { $replacements = [ // year 'წელი' => 'წლის', @@ -71,7 +71,7 @@ return "$time წინ"; }, - 'from_now' => function ($time) { + 'from_now' => static function ($time) { $replacements = [ // year 'წელი' => 'წელიწადში', @@ -93,7 +93,7 @@ return $time; }, - 'after' => function ($time) { + 'after' => static function ($time) { $replacements = [ // year 'წელი' => 'წლის', @@ -115,7 +115,7 @@ return "$time შემდეგ"; }, - 'before' => function ($time) { + 'before' => static function ($time) { $replacements = [ // year 'წელი' => 'წლით', @@ -152,14 +152,14 @@ 'calendar' => [ 'sameDay' => '[დღეს], LT[-ზე]', 'nextDay' => '[ხვალ], LT[-ზე]', - 'nextWeek' => function (CarbonInterface $current, CarbonInterface $other) { + 'nextWeek' => static function (CarbonInterface $current, \Carbon\CarbonInterface $other) { return ($current->isSameWeek($other) ? '' : '[შემდეგ] ').'dddd, LT[-ზე]'; }, 'lastDay' => '[გუშინ], LT[-ზე]', 'lastWeek' => '[წინა] dddd, LT-ზე', 'sameElse' => 'L', ], - 'ordinal' => function ($number) { + 'ordinal' => static function ($number) { if ($number === 0) { return $number; } @@ -184,7 +184,7 @@ 'first_day_of_week' => 1, 'day_of_first_week_of_year' => 1, 'list' => [', ', ' და '], - 'meridiem' => function ($hour) { + 'meridiem' => static function ($hour) { if ($hour >= 4) { if ($hour < 11) { return 'დილის'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kam.php b/vendor/nesbot/carbon/src/Carbon/Lang/kam.php index 0fc70d708..4d3a3b258 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/kam.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kam.php @@ -10,6 +10,7 @@ */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'meridiem' => ['Ĩyakwakya', 'Ĩyawĩoo'], 'weekdays' => ['Wa kyumwa', 'Wa kwambĩlĩlya', 'Wa kelĩ', 'Wa katatũ', 'Wa kana', 'Wa katano', 'Wa thanthatũ'], 'weekdays_short' => ['Wky', 'Wkw', 'Wkl', 'Wtũ', 'Wkn', 'Wtn', 'Wth'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ki.php b/vendor/nesbot/carbon/src/Carbon/Lang/ki.php index d86afc500..868fd61ae 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ki.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ki.php @@ -10,6 +10,7 @@ */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'meridiem' => ['Kiroko', 'Hwaĩ-inĩ'], 'weekdays' => ['Kiumia', 'Njumatatũ', 'Njumaine', 'Njumatana', 'Aramithi', 'Njumaa', 'Njumamothi'], 'weekdays_short' => ['KMA', 'NTT', 'NMN', 'NMT', 'ART', 'NMA', 'NMM'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kk.php b/vendor/nesbot/carbon/src/Carbon/Lang/kk.php index 59fa9affb..23452dd2a 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/kk.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kk.php @@ -66,7 +66,7 @@ 'lastWeek' => '[Өткен аптаның] dddd [сағат] LT', 'sameElse' => 'L', ], - 'ordinal' => function ($number) { + 'ordinal' => static function ($number) { static $suffixes = [ 0 => '-ші', 1 => '-ші', diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kkj.php b/vendor/nesbot/carbon/src/Carbon/Lang/kkj.php index 6a1e77a85..f4cdb67e9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/kkj.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kkj.php @@ -10,4 +10,5 @@ */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kl_GL.php b/vendor/nesbot/carbon/src/Carbon/Lang/kl_GL.php index 4fed720a3..64c4ae9c7 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/kl_GL.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kl_GL.php @@ -31,32 +31,32 @@ 'first_day_of_week' => 1, 'day_of_first_week_of_year' => 1, - 'year' => '{1}ukioq :count|{0}:count ukiut|]1,Inf[ukiut :count', - 'a_year' => '{1}ukioq|{0}:count ukiut|]1,Inf[ukiut :count', - 'y' => '{1}:countyr|{0}:countyrs|]1,Inf[:countyrs', + 'year' => '{1}ukioq :count|{0}:count ukiut|[-Inf,Inf]ukiut :count', + 'a_year' => '{1}ukioq|{0}:count ukiut|[-Inf,Inf]ukiut :count', + 'y' => '{1}:countyr|{0}:countyrs|[-Inf,Inf]:countyrs', - 'month' => '{1}qaammat :count|{0}:count qaammatit|]1,Inf[qaammatit :count', - 'a_month' => '{1}qaammat|{0}:count qaammatit|]1,Inf[qaammatit :count', - 'm' => '{1}:countmo|{0}:countmos|]1,Inf[:countmos', + 'month' => '{1}qaammat :count|{0}:count qaammatit|[-Inf,Inf]qaammatit :count', + 'a_month' => '{1}qaammat|{0}:count qaammatit|[-Inf,Inf]qaammatit :count', + 'm' => '{1}:countmo|{0}:countmos|[-Inf,Inf]:countmos', - 'week' => '{1}:count sap. ak.|{0}:count sap. ak.|]1,Inf[:count sap. ak.', - 'a_week' => '{1}a sap. ak.|{0}:count sap. ak.|]1,Inf[:count sap. ak.', + 'week' => '{1}:count sap. ak.|{0}:count sap. ak.|[-Inf,Inf]:count sap. ak.', + 'a_week' => '{1}a sap. ak.|{0}:count sap. ak.|[-Inf,Inf]:count sap. ak.', 'w' => ':countw', - 'day' => '{1}:count ulloq|{0}:count ullut|]1,Inf[:count ullut', - 'a_day' => '{1}a ulloq|{0}:count ullut|]1,Inf[:count ullut', + 'day' => '{1}:count ulloq|{0}:count ullut|[-Inf,Inf]:count ullut', + 'a_day' => '{1}a ulloq|{0}:count ullut|[-Inf,Inf]:count ullut', 'd' => ':countd', - 'hour' => '{1}:count tiimi|{0}:count tiimit|]1,Inf[:count tiimit', - 'a_hour' => '{1}tiimi|{0}:count tiimit|]1,Inf[:count tiimit', + 'hour' => '{1}:count tiimi|{0}:count tiimit|[-Inf,Inf]:count tiimit', + 'a_hour' => '{1}tiimi|{0}:count tiimit|[-Inf,Inf]:count tiimit', 'h' => ':counth', - 'minute' => '{1}:count minutsi|{0}:count minutsit|]1,Inf[:count minutsit', - 'a_minute' => '{1}a minutsi|{0}:count minutsit|]1,Inf[:count minutsit', + 'minute' => '{1}:count minutsi|{0}:count minutsit|[-Inf,Inf]:count minutsit', + 'a_minute' => '{1}a minutsi|{0}:count minutsit|[-Inf,Inf]:count minutsit', 'min' => ':countm', - 'second' => '{1}:count sikunti|{0}:count sikuntit|]1,Inf[:count sikuntit', - 'a_second' => '{1}sikunti|{0}:count sikuntit|]1,Inf[:count sikuntit', + 'second' => '{1}:count sikunti|{0}:count sikuntit|[-Inf,Inf]:count sikuntit', + 'a_second' => '{1}sikunti|{0}:count sikuntit|[-Inf,Inf]:count sikuntit', 's' => ':counts', 'ago' => ':time matuma siorna', diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kln.php b/vendor/nesbot/carbon/src/Carbon/Lang/kln.php index b9c39968b..de3f44fc5 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/kln.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kln.php @@ -10,6 +10,7 @@ */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'meridiem' => ['krn', 'koosk'], 'weekdays' => ['Kotisap', 'Kotaai', 'Koaeng’', 'Kosomok', 'Koang’wan', 'Komuut', 'Kolo'], 'weekdays_short' => ['Kts', 'Kot', 'Koo', 'Kos', 'Koa', 'Kom', 'Kol'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/km.php b/vendor/nesbot/carbon/src/Carbon/Lang/km.php index da790ac85..570703e20 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/km.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/km.php @@ -17,19 +17,25 @@ * - Sovichet Tep */ return [ - 'year' => '{1}មួយឆ្នាំ|]1,Inf[:count ឆ្នាំ', + 'year' => ':count ឆ្នាំ', + 'a_year' => '{1}មួយឆ្នាំ|[-Inf,Inf]:count ឆ្នាំ', 'y' => ':count ឆ្នាំ', - 'month' => '{1}មួយខែ|]1,Inf[:count ខែ', + 'month' => ':count ខែ', + 'a_month' => '{1}មួយខែ|[-Inf,Inf]:count ខែ', 'm' => ':count ខែ', - 'week' => ':count សប្ដាហ៍', - 'w' => ':count សប្ដាហ៍', - 'day' => '{1}មួយថ្ងៃ|]1,Inf[:count ថ្ងៃ', + 'week' => ':count សប្តាហ៍', + 'w' => ':count សប្តាហ៍', + 'day' => ':count ថ្ងៃ', + 'a_day' => '{1}មួយថ្ងៃ|[-Inf,Inf]:count ថ្ងៃ', 'd' => ':count ថ្ងៃ', - 'hour' => '{1}មួយម៉ោង|]1,Inf[:count ម៉ោង', + 'hour' => ':count ម៉ោង', + 'a_hour' => '{1}មួយម៉ោង|[-Inf,Inf]:count ម៉ោង', 'h' => ':count ម៉ោង', - 'minute' => '{1}មួយនាទី|]1,Inf[:count នាទី', + 'minute' => ':count នាទី', + 'a_minute' => '{1}មួយនាទី|[-Inf,Inf]:count នាទី', 'min' => ':count នាទី', - 'second' => '{1}ប៉ុន្មានវិនាទី|]1,Inf[:count វិនាទី', + 'second' => ':count វិនាទី', + 'a_second' => '{0,1}ប៉ុន្មានវិនាទី|[-Inf,Inf]:count វិនាទី', 's' => ':count វិនាទី', 'ago' => ':timeមុន', 'from_now' => ':timeទៀត', diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kn.php b/vendor/nesbot/carbon/src/Carbon/Lang/kn.php index 0d2ad08b4..51660e574 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/kn.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kn.php @@ -17,13 +17,20 @@ * - rajeevnaikte */ return [ - 'year' => '{1}ಒಂದು ವರ್ಷ|]1,Inf[:count ವರ್ಷ', - 'month' => '{1}ಒಂದು ತಿಂಗಳು|]1,Inf[:count ತಿಂಗಳು', - 'week' => '{1}ಒಂದು ವಾರ|]1,Inf[:count ವಾರಗಳು', - 'day' => '{1}ಒಂದು ದಿನ|]1,Inf[:count ದಿನ', - 'hour' => '{1}ಒಂದು ಗಂಟೆ|]1,Inf[:count ಗಂಟೆ', - 'minute' => '{1}ಒಂದು ನಿಮಿಷ|]1,Inf[:count ನಿಮಿಷ', - 'second' => '{1}ಕೆಲವು ಕ್ಷಣಗಳು|]1,Inf[:count ಸೆಕೆಂಡುಗಳು', + 'year' => '{1}:count ವರ್ಷ|[-Inf,Inf]:count ವರ್ಷಗಳು', + 'a_year' => '{1}ಒಂದು ವರ್ಷ|[-Inf,Inf]:count ವರ್ಷಗಳು', + 'month' => ':count ತಿಂಗಳು', + 'a_month' => '{1}ಒಂದು ತಿಂಗಳು|[-Inf,Inf]:count ತಿಂಗಳು', + 'week' => '{1}:count ವಾರ|[-Inf,Inf]:count ವಾರಗಳು', + 'a_week' => '{1}ಒಂದು ವಾರ|[-Inf,Inf]:count ವಾರಗಳು', + 'day' => '{1}:count ದಿನ|[-Inf,Inf]:count ದಿನಗಳು', + 'a_day' => '{1}ಒಂದು ದಿನ|[-Inf,Inf]:count ದಿನಗಳು', + 'hour' => '{1}:count ಗಂಟೆ|[-Inf,Inf]:count ಗಂಟೆಗಳು', + 'a_hour' => '{1}ಒಂದು ಗಂಟೆ|[-Inf,Inf]:count ಗಂಟೆಗಳು', + 'minute' => '{1}:count ನಿಮಿಷ|[-Inf,Inf]:count ನಿಮಿಷಗಳು', + 'a_minute' => '{1}ಒಂದು ನಿಮಿಷ|[-Inf,Inf]:count ನಿಮಿಷಗಳು', + 'second' => '{0,1}:count ಸೆಕೆಂಡ್|[-Inf,Inf]:count ಸೆಕೆಂಡುಗಳು', + 'a_second' => '{0,1}ಕೆಲವು ಕ್ಷಣಗಳು|[-Inf,Inf]:count ಸೆಕೆಂಡುಗಳು', 'ago' => ':time ಹಿಂದೆ', 'from_now' => ':time ನಂತರ', 'diff_now' => 'ಈಗ', @@ -47,7 +54,7 @@ 'sameElse' => 'L', ], 'ordinal' => ':numberನೇ', - 'meridiem' => function ($hour) { + 'meridiem' => static function ($hour) { if ($hour < 4) { return 'ರಾತ್ರಿ'; } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ko.php b/vendor/nesbot/carbon/src/Carbon/Lang/ko.php index 4fa623765..8581b716e 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ko.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ko.php @@ -22,25 +22,25 @@ */ return [ 'year' => ':count년', - 'a_year' => '{1}일년|]1,Inf[:count년', + 'a_year' => '{1}일년|[-Inf,Inf]:count년', 'y' => ':count년', 'month' => ':count개월', - 'a_month' => '{1}한달|]1,Inf[:count개월', + 'a_month' => '{1}한달|[-Inf,Inf]:count개월', 'm' => ':count개월', 'week' => ':count주', - 'a_week' => '{1}일주일|]1,Inf[:count 주', + 'a_week' => '{1}일주일|[-Inf,Inf]:count 주', 'w' => ':count주일', 'day' => ':count일', - 'a_day' => '{1}하루|]1,Inf[:count일', + 'a_day' => '{1}하루|[-Inf,Inf]:count일', 'd' => ':count일', 'hour' => ':count시간', - 'a_hour' => '{1}한시간|]1,Inf[:count시간', + 'a_hour' => '{1}한시간|[-Inf,Inf]:count시간', 'h' => ':count시간', 'minute' => ':count분', - 'a_minute' => '{1}일분|]1,Inf[:count분', + 'a_minute' => '{1}일분|[-Inf,Inf]:count분', 'min' => ':count분', 'second' => ':count초', - 'a_second' => '{1}몇초|]1,Inf[:count초', + 'a_second' => '{1}몇초|[-Inf,Inf]:count초', 's' => ':count초', 'ago' => ':time 전', 'from_now' => ':time 후', @@ -66,20 +66,13 @@ 'lastWeek' => '지난주 dddd LT', 'sameElse' => 'L', ], - 'ordinal' => function ($number, $period) { - switch ($period) { - case 'd': - case 'D': - case 'DDD': - return $number.'일'; - case 'M': - return $number.'월'; - case 'w': - case 'W': - return $number.'주'; - default: - return $number; - } + 'ordinal' => static function ($number, $period) { + return match ($period) { + 'd', 'D', 'DDD' => $number.'일', + 'M' => $number.'월', + 'w', 'W' => $number.'주', + default => $number, + }; }, 'meridiem' => ['오전', '오후'], 'months' => ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kok_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/kok_IN.php index 92ba844c5..c6110d595 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/kok_IN.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kok_IN.php @@ -22,6 +22,7 @@ 'weekdays' => ['आयतार', 'सोमार', 'मंगळवार', 'बुधवार', 'बेरेसतार', 'शुकरार', 'शेनवार'], 'weekdays_short' => ['आयतार', 'सोमार', 'मंगळवार', 'बुधवार', 'बेरेसतार', 'शुकरार', 'शेनवार'], 'weekdays_min' => ['आयतार', 'सोमार', 'मंगळवार', 'बुधवार', 'बेरेसतार', 'शुकरार', 'शेनवार'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['म.पू.', 'म.नं.'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ks_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ks_IN.php index ce9d5d4ae..4ec598fd1 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ks_IN.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ks_IN.php @@ -22,6 +22,7 @@ 'weekdays' => ['آتهوار', 'ژءندروار', 'بوءںوار', 'بودهوار', 'برىسوار', 'جمع', 'بٹوار'], 'weekdays_short' => ['آتهوار', 'ژءنتروار', 'بوءںوار', 'بودهوار', 'برىسوار', 'جمع', 'بٹوار'], 'weekdays_min' => ['آتهوار', 'ژءنتروار', 'بوءںوار', 'بودهوار', 'برىسوار', 'جمع', 'بٹوار'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['دوپھربرونھ', 'دوپھرپتھ'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ks_IN@devanagari.php b/vendor/nesbot/carbon/src/Carbon/Lang/ks_IN@devanagari.php index a2ae8b64f..0708f3ff1 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ks_IN@devanagari.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ks_IN@devanagari.php @@ -22,6 +22,7 @@ 'weekdays' => ['आथवार', 'चॅ़दुरवार', 'बोमवार', 'ब्वदवार', 'ब्रसवार', 'शोकुरवार', 'बटुवार'], 'weekdays_short' => ['आथ ', 'चॅ़दुर', 'बोम', 'ब्वद', 'ब्रस', 'शोकुर', 'बटु'], 'weekdays_min' => ['आथ ', 'चॅ़दुर', 'बोम', 'ब्वद', 'ब्रस', 'शोकुर', 'बटु'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['पूर्वाह्न', 'अपराह्न'], ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ku.php b/vendor/nesbot/carbon/src/Carbon/Lang/ku.php index 189960c8a..074b07609 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ku.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ku.php @@ -14,20 +14,36 @@ * - Unicode, Inc. */ -return [ +return array_replace_recursive(require __DIR__.'/en.php', [ 'ago' => 'berî :time', 'from_now' => 'di :time de', 'after' => ':time piştî', 'before' => ':time berê', 'year' => ':count sal', + 'a_year' => ':count sal', + 'y' => ':count sal', 'year_ago' => ':count salê|:count salan', + 'y_ago' => ':count salê|:count salan', 'year_from_now' => 'salekê|:count salan', + 'y_from_now' => 'salekê|:count salan', 'month' => ':count meh', + 'a_month' => ':count meh', + 'm' => ':count meh', 'week' => ':count hefte', + 'a_week' => ':count hefte', + 'w' => ':count hefte', 'day' => ':count roj', + 'a_day' => ':count roj', + 'd' => ':count roj', 'hour' => ':count saet', + 'a_hour' => ':count saet', + 'h' => ':count saet', 'minute' => ':count deqîqe', + 'a_minute' => ':count deqîqe', + 'min' => ':count deqîqe', 'second' => ':count saniye', + 'a_second' => ':count saniye', + 's' => ':count saniye', 'months' => ['rêbendanê', 'reşemiyê', 'adarê', 'avrêlê', 'gulanê', 'pûşperê', 'tîrmehê', 'gelawêjê', 'rezberê', 'kewçêrê', 'sermawezê', 'berfanbarê'], 'months_standalone' => ['rêbendan', 'reşemî', 'adar', 'avrêl', 'gulan', 'pûşper', 'tîrmeh', 'gelawêj', 'rezber', 'kewçêr', 'sermawez', 'berfanbar'], 'months_short' => ['rêb', 'reş', 'ada', 'avr', 'gul', 'pûş', 'tîr', 'gel', 'rez', 'kew', 'ser', 'ber'], @@ -35,6 +51,7 @@ 'weekdays_short' => ['yş', 'dş', 'sş', 'çş', 'pş', 'în', 'ş'], 'weekdays_min' => ['Y', 'D', 'S', 'Ç', 'P', 'Î', 'Ş'], 'list' => [', ', ' û '], + 'ordinal' => ':number', 'first_day_of_week' => 6, 'day_of_first_week_of_year' => 1, -]; +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ky.php b/vendor/nesbot/carbon/src/Carbon/Lang/ky.php index e0d1af103..2cb850395 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ky.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ky.php @@ -68,7 +68,7 @@ 'lastWeek' => '[Өткен аптанын] dddd [күнү] [саат] LT', 'sameElse' => 'L', ], - 'ordinal' => function ($number) { + 'ordinal' => static function ($number) { static $suffixes = [ 0 => '-чү', 1 => '-чи', diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lb.php b/vendor/nesbot/carbon/src/Carbon/Lang/lb.php index 7636655e3..72267b734 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/lb.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lb.php @@ -60,15 +60,12 @@ 'nextDay' => '[Muer um] LT', 'nextWeek' => 'dddd [um] LT', 'lastDay' => '[Gëschter um] LT', - 'lastWeek' => function (CarbonInterface $date) { + 'lastWeek' => static function (CarbonInterface $date) { // Different date string for 'Dënschdeg' (Tuesday) and 'Donneschdeg' (Thursday) due to phonological rule - switch ($date->dayOfWeek) { - case 2: - case 4: - return '[Leschten] dddd [um] LT'; - default: - return '[Leschte] dddd [um] LT'; - } + return match ($date->dayOfWeek) { + 2, 4 => '[Leschten] dddd [um] LT', + default => '[Leschte] dddd [um] LT', + }; }, 'sameElse' => 'L', ], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lkt.php b/vendor/nesbot/carbon/src/Carbon/Lang/lkt.php index ae73a97b9..a5485fb38 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/lkt.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lkt.php @@ -10,6 +10,7 @@ */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'month' => ':count haŋwí', // less reliable 'm' => ':count haŋwí', // less reliable diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lo.php b/vendor/nesbot/carbon/src/Carbon/Lang/lo.php index 48715f5c5..a5cc0242c 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/lo.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lo.php @@ -27,7 +27,8 @@ 'h' => ':count ຊມ. ', 'minute' => ':count ນາທີ', 'min' => ':count ນທ. ', - 'second' => '{1}ບໍ່ເທົ່າໃດວິນາທີ|]1,Inf[:count ວິນາທີ', + 'second' => ':count ວິນາທີ', + 'a_second' => '{0,1}ບໍ່ເທົ່າໃດວິນາທີ|[-Inf,Inf]:count ວິນາທີ', 's' => ':count ວິ. ', 'ago' => ':timeຜ່ານມາ', 'from_now' => 'ອີກ :time', diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lrc.php b/vendor/nesbot/carbon/src/Carbon/Lang/lrc.php index 546e67916..31cfc844d 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/lrc.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lrc.php @@ -10,6 +10,7 @@ */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'minute' => ':count هنر', // less reliable 'min' => ':count هنر', // less reliable diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lrc_IQ.php b/vendor/nesbot/carbon/src/Carbon/Lang/lrc_IQ.php index d42f5e971..1ae546b04 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/lrc_IQ.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lrc_IQ.php @@ -9,5 +9,4 @@ * file that was distributed with this source code. */ -return array_replace_recursive(require __DIR__.'/lrc.php', [ -]); +return require __DIR__.'/lrc.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lt.php b/vendor/nesbot/carbon/src/Carbon/Lang/lt.php index 7d1b6f743..207b817e0 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/lt.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lt.php @@ -121,15 +121,12 @@ 'lastWeek' => '[Paskutinį] dddd LT', 'sameElse' => 'L', ], - 'ordinal' => function ($number) { - switch ($number) { - case 0: - return '0-is'; - case 3: - return '3-ias'; - default: - return "$number-as"; - } + 'ordinal' => static function ($number) { + return match ($number) { + 0 => '0-is', + 3 => '3-ias', + default => "$number-as", + }; }, 'meridiem' => ['priešpiet', 'popiet'], ]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/luo.php b/vendor/nesbot/carbon/src/Carbon/Lang/luo.php index b55af7318..5d6ec7c9a 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/luo.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/luo.php @@ -10,6 +10,7 @@ */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'meridiem' => ['OD', 'OT'], 'weekdays' => ['Jumapil', 'Wuok Tich', 'Tich Ariyo', 'Tich Adek', 'Tich Ang’wen', 'Tich Abich', 'Ngeso'], 'weekdays_short' => ['JMP', 'WUT', 'TAR', 'TAD', 'TAN', 'TAB', 'NGS'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/luy.php b/vendor/nesbot/carbon/src/Carbon/Lang/luy.php index 2b37e3e32..ab92e842a 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/luy.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/luy.php @@ -10,6 +10,7 @@ */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'weekdays' => ['Jumapiri', 'Jumatatu', 'Jumanne', 'Jumatano', 'Murwa wa Kanne', 'Murwa wa Katano', 'Jumamosi'], 'weekdays_short' => ['J2', 'J3', 'J4', 'J5', 'Al', 'Ij', 'J1'], 'weekdays_min' => ['J2', 'J3', 'J4', 'J5', 'Al', 'Ij', 'J1'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lv.php b/vendor/nesbot/carbon/src/Carbon/Lang/lv.php index d5cba7ca0..6ff355125 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/lv.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lv.php @@ -47,40 +47,34 @@ $daysOfWeek = ['svētdiena', 'pirmdiena', 'otrdiena', 'trešdiena', 'ceturtdiena', 'piektdiena', 'sestdiena']; $daysOfWeekLocativum = ['svētdien', 'pirmdien', 'otrdien', 'trešdien', 'ceturtdien', 'piektdien', 'sestdien']; -$transformDiff = function ($input) { - return strtr($input, [ - // Nominative => "pirms/pēc" Dative - 'gads' => 'gada', - 'gadi' => 'gadiem', - 'gadu' => 'gadiem', - 'mēnesis' => 'mēneša', - 'mēneši' => 'mēnešiem', - 'mēnešu' => 'mēnešiem', - 'nedēļa' => 'nedēļas', - 'nedēļas' => 'nedēļām', - 'nedēļu' => 'nedēļām', - 'diena' => 'dienas', - 'dienas' => 'dienām', - 'dienu' => 'dienām', - 'stunda' => 'stundas', - 'stundas' => 'stundām', - 'stundu' => 'stundām', - 'minūte' => 'minūtes', - 'minūtes' => 'minūtēm', - 'minūšu' => 'minūtēm', - 'sekunde' => 'sekundes', - 'sekundes' => 'sekundēm', - 'sekunžu' => 'sekundēm', - ]); -}; +$transformDiff = static fn (string $input) => strtr($input, [ + // Nominative => "pirms/pēc" Dative + 'gads' => 'gada', + 'gadi' => 'gadiem', + 'gadu' => 'gadiem', + 'mēnesis' => 'mēneša', + 'mēneši' => 'mēnešiem', + 'mēnešu' => 'mēnešiem', + 'nedēļa' => 'nedēļas', + 'nedēļas' => 'nedēļām', + 'nedēļu' => 'nedēļām', + 'diena' => 'dienas', + 'dienas' => 'dienām', + 'dienu' => 'dienām', + 'stunda' => 'stundas', + 'stundas' => 'stundām', + 'stundu' => 'stundām', + 'minūte' => 'minūtes', + 'minūtes' => 'minūtēm', + 'minūšu' => 'minūtēm', + 'sekunde' => 'sekundes', + 'sekundes' => 'sekundēm', + 'sekunžu' => 'sekundēm', +]); return [ - 'ago' => function ($time) use ($transformDiff) { - return 'pirms '.$transformDiff($time); - }, - 'from_now' => function ($time) use ($transformDiff) { - return 'pēc '.$transformDiff($time); - }, + 'ago' => static fn (string $time) => 'pirms '.$transformDiff($time), + 'from_now' => static fn (string $time) => 'pēc '.$transformDiff($time), 'year' => '0 gadu|:count gads|:count gadi', 'y' => ':count g.', @@ -159,7 +153,7 @@ 'calendar' => [ 'sameDay' => '[šodien] [plkst.] LT', 'nextDay' => '[rīt] [plkst.] LT', - 'nextWeek' => function (CarbonInterface $current, CarbonInterface $other) use ($daysOfWeekLocativum) { + 'nextWeek' => static function (CarbonInterface $current, CarbonInterface $other) use ($daysOfWeekLocativum) { if ($current->week !== $other->week) { return '[nākošo] ['.$daysOfWeekLocativum[$current->dayOfWeek].'] [plkst.] LT'; } @@ -167,7 +161,7 @@ return '['.$daysOfWeekLocativum[$current->dayOfWeek].'] [plkst.] LT'; }, 'lastDay' => '[vakar] [plkst.] LT', - 'lastWeek' => function (CarbonInterface $current) use ($daysOfWeekLocativum) { + 'lastWeek' => static function (CarbonInterface $current) use ($daysOfWeekLocativum) { return '[pagājušo] ['.$daysOfWeekLocativum[$current->dayOfWeek].'] [plkst.] LT'; }, 'sameElse' => 'L', diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lzh_TW.php b/vendor/nesbot/carbon/src/Carbon/Lang/lzh_TW.php index 3b1493eef..771394e83 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/lzh_TW.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lzh_TW.php @@ -22,6 +22,7 @@ 'weekdays' => ['週日', '週一', '週二', '週三', '週四', '週五', '週六'], 'weekdays_short' => ['日', '一', '二', '三', '四', '五', '六'], 'weekdays_min' => ['日', '一', '二', '三', '四', '五', '六'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'alt_numbers' => ['〇', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '廿', '廿一', '廿二', '廿三', '廿四', '廿五', '廿六', '廿七', '廿八', '廿九', '卅', '卅一'], 'meridiem' => ['朝', '暮'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mag_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/mag_IN.php index 193f67a7e..879776541 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/mag_IN.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mag_IN.php @@ -22,6 +22,7 @@ 'weekdays' => ['एतवार', 'सोमार', 'मंगर', 'बुध', 'बिफे', 'सूक', 'सनिचर'], 'weekdays_short' => ['एतवार', 'सोमार', 'मंगर', 'बुध', 'बिफे', 'सूक', 'सनिचर'], 'weekdays_min' => ['एतवार', 'सोमार', 'मंगर', 'बुध', 'बिफे', 'सूक', 'सनिचर'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['पूर्वाह्न', 'अपराह्न'], ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mai_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/mai_IN.php index 03049d458..3f9bba779 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/mai_IN.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mai_IN.php @@ -22,6 +22,7 @@ 'weekdays' => ['रविदिन', 'सोमदिन', 'मंगलदिन', 'बुधदिन', 'बृहस्पतीदिन', 'शुक्रदिन', 'शनीदिन'], 'weekdays_short' => ['रवि', 'सोम', 'मंगल', 'बुध', 'बृहस्पती', 'शुक्र', 'शनी'], 'weekdays_min' => ['रवि', 'सोम', 'मंगल', 'बुध', 'बृहस्पती', 'शुक्र', 'शनी'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['पूर्वाह्न', 'अपराह्न'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mas.php b/vendor/nesbot/carbon/src/Carbon/Lang/mas.php index cbd610c28..ba99156e9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/mas.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mas.php @@ -10,6 +10,7 @@ */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'meridiem' => ['Ɛnkakɛnyá', 'Ɛndámâ'], 'weekdays' => ['Jumapílí', 'Jumatátu', 'Jumane', 'Jumatánɔ', 'Alaámisi', 'Jumáa', 'Jumamósi'], 'weekdays_short' => ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mer.php b/vendor/nesbot/carbon/src/Carbon/Lang/mer.php index 2e14597fa..9b4ad3be9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/mer.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mer.php @@ -10,6 +10,7 @@ */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'meridiem' => ['RŨ', 'ŨG'], 'weekdays' => ['Kiumia', 'Muramuko', 'Wairi', 'Wethatu', 'Wena', 'Wetano', 'Jumamosi'], 'weekdays_short' => ['KIU', 'MRA', 'WAI', 'WET', 'WEN', 'WTN', 'JUM'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mfe_MU.php b/vendor/nesbot/carbon/src/Carbon/Lang/mfe_MU.php index 2d27b4578..ef51ce7dc 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/mfe_MU.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mfe_MU.php @@ -14,6 +14,7 @@ * - Samsung Electronics Co., Ltd. akhilesh.k@samsung.com */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'formats' => [ 'L' => 'DD/MM/YY', ], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mgh.php b/vendor/nesbot/carbon/src/Carbon/Lang/mgh.php index 2a80960d7..a4b624c46 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/mgh.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mgh.php @@ -10,6 +10,7 @@ */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'meridiem' => ['wichishu', 'mchochil’l'], 'weekdays' => ['Sabato', 'Jumatatu', 'Jumanne', 'Jumatano', 'Arahamisi', 'Ijumaa', 'Jumamosi'], 'weekdays_short' => ['Sab', 'Jtt', 'Jnn', 'Jtn', 'Ara', 'Iju', 'Jmo'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mk.php b/vendor/nesbot/carbon/src/Carbon/Lang/mk.php index d822de098..38fe6d046 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/mk.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mk.php @@ -68,19 +68,13 @@ 'nextDay' => '[Утре во] LT', 'nextWeek' => '[Во] dddd [во] LT', 'lastDay' => '[Вчера во] LT', - 'lastWeek' => function (CarbonInterface $date) { - switch ($date->dayOfWeek) { - case 0: - case 3: - case 6: - return '[Изминатата] dddd [во] LT'; - default: - return '[Изминатиот] dddd [во] LT'; - } + 'lastWeek' => static fn (CarbonInterface $date) => match ($date->dayOfWeek) { + 0, 3, 6 => '[Изминатата] dddd [во] LT', + default => '[Изминатиот] dddd [во] LT', }, 'sameElse' => 'L', ], - 'ordinal' => function ($number) { + 'ordinal' => static function ($number) { $lastDigit = $number % 10; $last2Digits = $number % 100; if ($number === 0) { diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ml.php b/vendor/nesbot/carbon/src/Carbon/Lang/ml.php index 1abd6c449..a35f96f50 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ml.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ml.php @@ -50,7 +50,7 @@ 'lastWeek' => '[കഴിഞ്ഞ] dddd, LT', 'sameElse' => 'L', ], - 'meridiem' => function ($hour) { + 'meridiem' => static function ($hour) { if ($hour < 4) { return 'രാത്രി'; } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mni_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/mni_IN.php index 45d430ef9..4dc577c38 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/mni_IN.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mni_IN.php @@ -22,6 +22,7 @@ 'weekdays' => ['নোংমাইজিং', 'নিংথৌকাবা', 'লৈবাকপোকপা', 'য়ুমশকৈশা', 'শগোলশেন', 'ইরাই', 'থাংজ'], 'weekdays_short' => ['নোং', 'নিং', 'লৈবাক', 'য়ুম', 'শগোল', 'ইরা', 'থাং'], 'weekdays_min' => ['নোং', 'নিং', 'লৈবাক', 'য়ুম', 'শগোল', 'ইরা', 'থাং'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['এ.ম.', 'প.ম.'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mr.php b/vendor/nesbot/carbon/src/Carbon/Lang/mr.php index 4aaeafd0c..e57e6f514 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/mr.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mr.php @@ -57,7 +57,7 @@ 'sameElse' => 'L', ], - 'meridiem' => function ($hour) { + 'meridiem' => static function ($hour) { if ($hour < 4) { return 'रात्री'; } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ms.php b/vendor/nesbot/carbon/src/Carbon/Lang/ms.php index c9e80854f..fc53ab982 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ms.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ms.php @@ -21,29 +21,29 @@ */ return [ 'year' => ':count tahun', - 'a_year' => '{1}setahun|]1,Inf[:count tahun', + 'a_year' => '{1}setahun|[-Inf,Inf]:count tahun', 'y' => ':count tahun', 'month' => ':count bulan', - 'a_month' => '{1}sebulan|]1,Inf[:count bulan', + 'a_month' => '{1}sebulan|[-Inf,Inf]:count bulan', 'm' => ':count bulan', 'week' => ':count minggu', - 'a_week' => '{1}seminggu|]1,Inf[:count minggu', + 'a_week' => '{1}seminggu|[-Inf,Inf]:count minggu', 'w' => ':count minggu', 'day' => ':count hari', - 'a_day' => '{1}sehari|]1,Inf[:count hari', + 'a_day' => '{1}sehari|[-Inf,Inf]:count hari', 'd' => ':count hari', 'hour' => ':count jam', - 'a_hour' => '{1}sejam|]1,Inf[:count jam', + 'a_hour' => '{1}sejam|[-Inf,Inf]:count jam', 'h' => ':count jam', 'minute' => ':count minit', - 'a_minute' => '{1}seminit|]1,Inf[:count minit', + 'a_minute' => '{1}seminit|[-Inf,Inf]:count minit', 'min' => ':count minit', 'second' => ':count saat', - 'a_second' => '{1}beberapa saat|]1,Inf[:count saat', + 'a_second' => '{1}beberapa saat|[-Inf,Inf]:count saat', 'millisecond' => ':count milisaat', - 'a_millisecond' => '{1}semilisaat|]1,Inf[:count milliseconds', + 'a_millisecond' => '{1}semilisaat|[-Inf,Inf]:count milliseconds', 'microsecond' => ':count mikrodetik', - 'a_microsecond' => '{1}semikrodetik|]1,Inf[:count mikrodetik', + 'a_microsecond' => '{1}semikrodetik|[-Inf,Inf]:count mikrodetik', 's' => ':count saat', 'ago' => ':time yang lepas', 'from_now' => ':time dari sekarang', @@ -74,7 +74,7 @@ 'lastWeek' => 'dddd [lepas pukul] LT', 'sameElse' => 'L', ], - 'meridiem' => function ($hour) { + 'meridiem' => static function ($hour) { if ($hour < 1) { return 'tengah malam'; } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/my.php b/vendor/nesbot/carbon/src/Carbon/Lang/my.php index bbdfba401..fe0b252bd 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/my.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/my.php @@ -16,19 +16,25 @@ * - Nay Lin Aung */ return [ - 'year' => '{1}တစ်နှစ်|]1,Inf[:count နှစ်', + 'year' => ':count နှစ်', + 'a_year' => '{1}တစ်နှစ်|[-Inf,Inf]:count နှစ်', 'y' => ':count နှစ်', - 'month' => '{1}တစ်လ|]1,Inf[:count လ', + 'month' => ':count လ', + 'a_month' => '{1}တစ်လ|[-Inf,Inf]:count လ', 'm' => ':count လ', 'week' => ':count ပတ်', 'w' => ':count ပတ်', - 'day' => '{1}တစ်ရက်|]1,Inf[:count ရက်', + 'day' => ':count ရက်', + 'a_day' => '{1}တစ်ရက်|[-Inf,Inf]:count ရက်', 'd' => ':count ရက်', - 'hour' => '{1}တစ်နာရီ|]1,Inf[:count နာရီ', + 'hour' => ':count နာရီ', + 'a_hour' => '{1}တစ်နာရီ|[-Inf,Inf]:count နာရီ', 'h' => ':count နာရီ', - 'minute' => '{1}တစ်မိနစ်|]1,Inf[:count မိနစ်', + 'minute' => ':count မိနစ်', + 'a_minute' => '{1}တစ်မိနစ်|[-Inf,Inf]:count မိနစ်', 'min' => ':count မိနစ်', - 'second' => '{1}စက္ကန်.အနည်းငယ်|]1,Inf[:count စက္ကန့်', + 'second' => ':count စက္ကန့်', + 'a_second' => '{0,1}စက္ကန်.အနည်းငယ်|[-Inf,Inf]:count စက္ကန့်', 's' => ':count စက္ကန့်', 'ago' => 'လွန်ခဲ့သော :time က', 'from_now' => 'လာမည့် :time မှာ', diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nan_TW.php b/vendor/nesbot/carbon/src/Carbon/Lang/nan_TW.php index 5c50aa48d..4fc654816 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/nan_TW.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nan_TW.php @@ -22,6 +22,7 @@ 'weekdays' => ['禮拜日', '禮拜一', '禮拜二', '禮拜三', '禮拜四', '禮拜五', '禮拜六'], 'weekdays_short' => ['日', '一', '二', '三', '四', '五', '六'], 'weekdays_min' => ['日', '一', '二', '三', '四', '五', '六'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['頂晡', '下晡'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nan_TW@latin.php b/vendor/nesbot/carbon/src/Carbon/Lang/nan_TW@latin.php index 99ca2a425..5eecc63a8 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/nan_TW@latin.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nan_TW@latin.php @@ -22,6 +22,7 @@ 'weekdays' => ['lé-pài-ji̍t', 'pài-it', 'pài-jī', 'pài-saⁿ', 'pài-sì', 'pài-gō͘', 'pài-la̍k'], 'weekdays_short' => ['lp', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6'], 'weekdays_min' => ['lp', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['téng-po͘', 'ē-po͘'], ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nd.php b/vendor/nesbot/carbon/src/Carbon/Lang/nd.php index f75d9a714..d88633c27 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/nd.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nd.php @@ -10,6 +10,7 @@ */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'weekdays' => ['Sonto', 'Mvulo', 'Sibili', 'Sithathu', 'Sine', 'Sihlanu', 'Mgqibelo'], 'weekdays_short' => ['Son', 'Mvu', 'Sib', 'Sit', 'Sin', 'Sih', 'Mgq'], 'weekdays_min' => ['Son', 'Mvu', 'Sib', 'Sit', 'Sin', 'Sih', 'Mgq'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ne.php b/vendor/nesbot/carbon/src/Carbon/Lang/ne.php index d4caf0e25..998d13f66 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ne.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ne.php @@ -55,7 +55,7 @@ 'lastWeek' => '[गएको] dddd[,] LT', 'sameElse' => 'L', ], - 'meridiem' => function ($hour) { + 'meridiem' => static function ($hour) { if ($hour < 3) { return 'राति'; } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nhn_MX.php b/vendor/nesbot/carbon/src/Carbon/Lang/nhn_MX.php index 9db88a128..8d06d5092 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/nhn_MX.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nhn_MX.php @@ -22,6 +22,7 @@ 'weekdays' => ['teoilhuitl', 'ceilhuitl', 'omeilhuitl', 'yeilhuitl', 'nahuilhuitl', 'macuililhuitl', 'chicuaceilhuitl'], 'weekdays_short' => ['teo', 'cei', 'ome', 'yei', 'nau', 'mac', 'chi'], 'weekdays_min' => ['teo', 'cei', 'ome', 'yei', 'nau', 'mac', 'chi'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'month' => ':count metztli', // less reliable diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nl.php b/vendor/nesbot/carbon/src/Carbon/Lang/nl.php index 2d737703d..ccf192577 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/nl.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nl.php @@ -69,7 +69,7 @@ 'diff_after_tomorrow' => 'overmorgen', 'diff_before_yesterday' => 'eergisteren', 'period_recurrences' => ':count keer', - 'period_interval' => function (string $interval = '') { + 'period_interval' => static function (string $interval = '') { /** @var string $output */ $output = preg_replace('/^(een|één|1)\s+/u', '', $interval); @@ -97,7 +97,7 @@ 'lastWeek' => '[afgelopen] dddd [om] LT', 'sameElse' => 'L', ], - 'ordinal' => function ($number) { + 'ordinal' => static function ($number) { return $number.(($number === 1 || $number === 8 || $number >= 20) ? 'ste' : 'de'); }, 'months' => ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nr_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/nr_ZA.php index f9a7be82e..509375982 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/nr_ZA.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nr_ZA.php @@ -23,4 +23,5 @@ 'weekdays_short' => ['Son', 'Mvu', 'Bil', 'Tha', 'Ne', 'Hla', 'Gqi'], 'weekdays_min' => ['Son', 'Mvu', 'Bil', 'Tha', 'Ne', 'Hla', 'Gqi'], 'day_of_first_week_of_year' => 1, + 'first_day_of_week' => 0, ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nso_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/nso_ZA.php index b08fe6dcd..93da1e78a 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/nso_ZA.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nso_ZA.php @@ -22,6 +22,7 @@ 'weekdays' => ['LaMorena', 'Mošupologo', 'Labobedi', 'Laboraro', 'Labone', 'Labohlano', 'Mokibelo'], 'weekdays_short' => ['Son', 'Moš', 'Bed', 'Rar', 'Ne', 'Hla', 'Mok'], 'weekdays_min' => ['Son', 'Moš', 'Bed', 'Rar', 'Ne', 'Hla', 'Mok'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'year' => ':count ngwaga', diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/oc.php b/vendor/nesbot/carbon/src/Carbon/Lang/oc.php index c9411d69d..858cf77be 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/oc.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/oc.php @@ -12,6 +12,7 @@ /* * Authors: * - Quentí + * - Quentin PAGÈS */ // @codeCoverageIgnoreStart use Symfony\Component\Translation\PluralizationRules; @@ -84,11 +85,11 @@ 'weekdays' => ['dimenge', 'diluns', 'dimars', 'dimècres', 'dijòus', 'divendres', 'dissabte'], 'weekdays_short' => ['dg', 'dl', 'dm', 'dc', 'dj', 'dv', 'ds'], 'weekdays_min' => ['dg', 'dl', 'dm', 'dc', 'dj', 'dv', 'ds'], - 'ordinal' => function ($number, string $period = '') { + 'ordinal' => static function ($number, string $period = '') { $ordinal = [1 => 'èr', 2 => 'nd'][(int) $number] ?? 'en'; - // feminine for year, week, hour, minute, second - if (preg_match('/^[yYwWhHgGis]$/', $period)) { + // feminine for week, hour, minute, second + if (preg_match('/^[wWhHgGis]$/', $period)) { $ordinal .= 'a'; } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/om.php b/vendor/nesbot/carbon/src/Carbon/Lang/om.php index b8d5a0b01..3c72dc9e5 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/om.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/om.php @@ -27,6 +27,7 @@ 'weekdays' => ['Dilbata', 'Wiixata', 'Qibxata', 'Roobii', 'Kamiisa', 'Jimaata', 'Sanbata'], 'weekdays_short' => ['Dil', 'Wix', 'Qib', 'Rob', 'Kam', 'Jim', 'San'], 'weekdays_min' => ['Dil', 'Wix', 'Qib', 'Rob', 'Kam', 'Jim', 'San'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['WD', 'WB'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pa.php b/vendor/nesbot/carbon/src/Carbon/Lang/pa.php index 48b203316..23c2f9e4c 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/pa.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pa.php @@ -47,7 +47,7 @@ 'lastWeek' => '[ਪਿਛਲੇ] dddd, LT', 'sameElse' => 'L', ], - 'meridiem' => function ($hour) { + 'meridiem' => static function ($hour) { if ($hour < 4) { return 'ਰਾਤ'; } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pa_PK.php b/vendor/nesbot/carbon/src/Carbon/Lang/pa_PK.php index f9af11c60..494a87767 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/pa_PK.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pa_PK.php @@ -22,6 +22,7 @@ 'weekdays' => ['اتوار', 'پير', 'منگل', 'بدھ', 'جمعرات', 'جمعه', 'هفته'], 'weekdays_short' => ['اتوار', 'پير', 'منگل', 'بدھ', 'جمعرات', 'جمعه', 'هفته'], 'weekdays_min' => ['اتوار', 'پير', 'منگل', 'بدھ', 'جمعرات', 'جمعه', 'هفته'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['ص', 'ش'], ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pl.php b/vendor/nesbot/carbon/src/Carbon/Lang/pl.php index b72053549..35381f33a 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/pl.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pl.php @@ -82,32 +82,19 @@ 'calendar' => [ 'sameDay' => '[Dziś o] LT', 'nextDay' => '[Jutro o] LT', - 'nextWeek' => function (CarbonInterface $date) { - switch ($date->dayOfWeek) { - case 0: - return '[W niedzielę o] LT'; - case 2: - return '[We wtorek o] LT'; - case 3: - return '[W środę o] LT'; - case 6: - return '[W sobotę o] LT'; - default: - return '[W] dddd [o] LT'; - } + 'nextWeek' => static fn (CarbonInterface $date) => match ($date->dayOfWeek) { + 0 => '[W niedzielę o] LT', + 2 => '[We wtorek o] LT', + 3 => '[W środę o] LT', + 6 => '[W sobotę o] LT', + default => '[W] dddd [o] LT', }, 'lastDay' => '[Wczoraj o] LT', - 'lastWeek' => function (CarbonInterface $date) { - switch ($date->dayOfWeek) { - case 0: - return '[W zeszłą niedzielę o] LT'; - case 3: - return '[W zeszłą środę o] LT'; - case 6: - return '[W zeszłą sobotę o] LT'; - default: - return '[W zeszły] dddd [o] LT'; - } + 'lastWeek' => static fn (CarbonInterface $date) => match ($date->dayOfWeek) { + 0 => '[W zeszłą niedzielę o] LT', + 3 => '[W zeszłą środę o] LT', + 6 => '[W zeszłą sobotę o] LT', + default => '[W zeszły] dddd [o] LT', }, 'sameElse' => 'L', ], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt.php index bb6359b14..85dbddccd 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/pt.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt.php @@ -84,14 +84,9 @@ 'nextDay' => '[Amanhã às] LT', 'nextWeek' => 'dddd [às] LT', 'lastDay' => '[Ontem às] LT', - 'lastWeek' => function (CarbonInterface $date) { - switch ($date->dayOfWeek) { - case 0: - case 6: - return '[Último] dddd [às] LT'; - default: - return '[Última] dddd [às] LT'; - } + 'lastWeek' => static fn (CarbonInterface $date) => match ($date->dayOfWeek) { + 0, 6 => '[Último] dddd [às] LT', + default => '[Última] dddd [às] LT', }, 'sameElse' => 'L', ], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/quz_PE.php b/vendor/nesbot/carbon/src/Carbon/Lang/quz_PE.php index d32291893..b047e5970 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/quz_PE.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/quz_PE.php @@ -22,6 +22,7 @@ 'weekdays' => ['tuminku', 'lunis', 'martis', 'miyirkulis', 'juywis', 'wiyirnis', 'sawatu'], 'weekdays_short' => ['tum', 'lun', 'mar', 'miy', 'juy', 'wiy', 'saw'], 'weekdays_min' => ['tum', 'lun', 'mar', 'miy', 'juy', 'wiy', 'saw'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'minute' => ':count uchuy', // less reliable diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/raj_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/raj_IN.php index 7b4589cd3..4a9f0b9d3 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/raj_IN.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/raj_IN.php @@ -22,6 +22,7 @@ 'weekdays' => ['रविवार', 'सोमवार', 'मंगल्लवार', 'बुधवार', 'बृहस्पतिवार', 'शुक्रवार', 'शनिवार'], 'weekdays_short' => ['रवि', 'सोम', 'मंगल', 'बुध', 'बृहस्पति', 'शुक्र', 'शनि'], 'weekdays_min' => ['रवि', 'सोम', 'मंगल', 'बुध', 'बृहस्पति', 'शुक्र', 'शनि'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['पूर्वाह्न', 'अपराह्न'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ru.php b/vendor/nesbot/carbon/src/Carbon/Lang/ru.php index 673b043b2..fe9607f46 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ru.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ru.php @@ -34,17 +34,16 @@ * - AlexWalkerson * - Vladislav UnsealedOne * - dima-bzz + * - Sergey Danilchenko */ use Carbon\CarbonInterface; -$transformDiff = function ($input) { - return strtr($input, [ - 'неделя' => 'неделю', - 'секунда' => 'секунду', - 'минута' => 'минуту', - ]); -}; +$transformDiff = static fn (string $input) => strtr($input, [ + 'неделя' => 'неделю', + 'секунда' => 'секунду', + 'минута' => 'минуту', +]); return [ 'year' => ':count год|:count года|:count лет', @@ -68,18 +67,15 @@ 'second' => ':count секунда|:count секунды|:count секунд', 's' => ':count сек.', 'a_second' => '{1}несколько секунд|:count секунду|:count секунды|:count секунд', - 'ago' => function ($time) use ($transformDiff) { - return $transformDiff($time).' назад'; - }, - 'from_now' => function ($time) use ($transformDiff) { - return 'через '.$transformDiff($time); - }, - 'after' => function ($time) use ($transformDiff) { - return $transformDiff($time).' после'; - }, - 'before' => function ($time) use ($transformDiff) { - return $transformDiff($time).' до'; - }, + 'millisecond' => '{1}:count миллисекунда|:count миллисекунды|:count миллисекунд', + 'a_millisecond' => '{1}миллисекунда|:count миллисекунда|:count миллисекунды|:count миллисекунд', + 'ms' => ':count мс', + 'microsecond' => '{1}:count микросекунда|:count микросекунды|:count микросекунд', + 'a_microsecond' => '{1}микросекунда|:count микросекунда|:count микросекунды|:count микросекунд', + 'ago' => static fn (string $time) => $transformDiff($time).' назад', + 'from_now' => static fn (string $time) => 'через '.$transformDiff($time), + 'after' => static fn (string $time) => $transformDiff($time).' после', + 'before' => static fn (string $time) => $transformDiff($time).' до', 'diff_now' => 'только что', 'diff_today' => 'Сегодня,', 'diff_today_regexp' => 'Сегодня,?(?:\\s+в)?', @@ -100,7 +96,7 @@ 'calendar' => [ 'sameDay' => '[Сегодня, в] LT', 'nextDay' => '[Завтра, в] LT', - 'nextWeek' => function (CarbonInterface $current, CarbonInterface $other) { + 'nextWeek' => static function (CarbonInterface $current, \Carbon\CarbonInterface $other) { if ($current->week !== $other->week) { switch ($current->dayOfWeek) { case 0: @@ -123,7 +119,7 @@ return '[В] dddd, [в] LT'; }, 'lastDay' => '[Вчера, в] LT', - 'lastWeek' => function (CarbonInterface $current, CarbonInterface $other) { + 'lastWeek' => static function (CarbonInterface $current, \Carbon\CarbonInterface $other) { if ($current->week !== $other->week) { switch ($current->dayOfWeek) { case 0: @@ -147,22 +143,15 @@ }, 'sameElse' => 'L', ], - 'ordinal' => function ($number, $period) { - switch ($period) { - case 'M': - case 'd': - case 'DDD': - return $number.'-й'; - case 'D': - return $number.'-го'; - case 'w': - case 'W': - return $number.'-я'; - default: - return $number; - } + 'ordinal' => static function ($number, $period) { + return match ($period) { + 'M', 'd', 'DDD' => $number.'-й', + 'D' => $number.'-го', + 'w', 'W' => $number.'-я', + default => $number, + }; }, - 'meridiem' => function ($hour) { + 'meridiem' => static function ($hour) { if ($hour < 4) { return 'ночи'; } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sa_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/sa_IN.php index cfda9a636..f2489e8de 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/sa_IN.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sa_IN.php @@ -22,6 +22,7 @@ 'weekdays' => ['रविवासर:', 'सोमवासर:', 'मंगलवासर:', 'बुधवासर:', 'बृहस्पतिवासरः', 'शुक्रवासर', 'शनिवासर:'], 'weekdays_short' => ['रविः', 'सोम:', 'मंगल:', 'बुध:', 'बृहस्पतिः', 'शुक्र', 'शनि:'], 'weekdays_min' => ['रविः', 'सोम:', 'मंगल:', 'बुध:', 'बृहस्पतिः', 'शुक्र', 'शनि:'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['पूर्वाह्न', 'अपराह्न'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/saq.php b/vendor/nesbot/carbon/src/Carbon/Lang/saq.php index ff8bf6044..ca3f994d4 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/saq.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/saq.php @@ -10,6 +10,7 @@ */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'meridiem' => ['Tesiran', 'Teipa'], 'weekdays' => ['Mderot ee are', 'Mderot ee kuni', 'Mderot ee ong’wan', 'Mderot ee inet', 'Mderot ee ile', 'Mderot ee sapa', 'Mderot ee kwe'], 'weekdays_short' => ['Are', 'Kun', 'Ong', 'Ine', 'Ile', 'Sap', 'Kwe'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sat_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/sat_IN.php index 632b1af6b..6c3608b2d 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/sat_IN.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sat_IN.php @@ -22,6 +22,7 @@ 'weekdays' => ['सिंगेमाँहाँ', 'ओतेमाँहाँ', 'बालेमाँहाँ', 'सागुनमाँहाँ', 'सारदीमाँहाँ', 'जारुममाँहाँ', 'ञुहुममाँहाँ'], 'weekdays_short' => ['सिंगे', 'ओते', 'बाले', 'सागुन', 'सारदी', 'जारुम', 'ञुहुम'], 'weekdays_min' => ['सिंगे', 'ओते', 'बाले', 'सागुन', 'सारदी', 'जारुम', 'ञुहुम'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'month' => ':count ńindạ cando', // less reliable diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sd.php b/vendor/nesbot/carbon/src/Carbon/Lang/sd.php index 0022c5a9c..d91e4d852 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/sd.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sd.php @@ -38,16 +38,22 @@ * Authors: * - Narain Sagar * - Sawood Alam - * - Narain Sagar */ return [ - 'year' => '{1}'.'هڪ سال'.'|:count '.'سال', - 'month' => '{1}'.'هڪ مهينو'.'|:count '.'مهينا', - 'week' => '{1}'.'ھڪ ھفتو'.'|:count '.'هفتا', - 'day' => '{1}'.'هڪ ڏينهن'.'|:count '.'ڏينهن', - 'hour' => '{1}'.'هڪ ڪلاڪ'.'|:count '.'ڪلاڪ', - 'minute' => '{1}'.'هڪ منٽ'.'|:count '.'منٽ', - 'second' => '{1}'.'چند سيڪنڊ'.'|:count '.'سيڪنڊ', + 'year' => ':count '.'سال', + 'a_year' => '{1}'.'هڪ سال'.'|:count '.'سال', + 'month' => ':count '.'مهينا', + 'a_month' => '{1}'.'هڪ مهينو'.'|:count '.'مهينا', + 'week' => ':count '.'هفتا', + 'a_week' => '{1}'.'ھڪ ھفتو'.'|:count '.'هفتا', + 'day' => ':count '.'ڏينهن', + 'a_day' => '{1}'.'هڪ ڏينهن'.'|:count '.'ڏينهن', + 'hour' => ':count '.'ڪلاڪ', + 'a_hour' => '{1}'.'هڪ ڪلاڪ'.'|:count '.'ڪلاڪ', + 'minute' => ':count '.'منٽ', + 'a_minute' => '{1}'.'هڪ منٽ'.'|:count '.'منٽ', + 'second' => ':count '.'سيڪنڊ', + 'a_second' => '{1}'.'چند سيڪنڊ'.'|:count '.'سيڪنڊ', 'ago' => ':time اڳ', 'from_now' => ':time پوء', 'diff_yesterday' => 'ڪالهه', diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/seh.php b/vendor/nesbot/carbon/src/Carbon/Lang/seh.php index babf9afb0..31b5aadea 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/seh.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/seh.php @@ -10,6 +10,7 @@ */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'weekdays' => ['Dimingu', 'Chiposi', 'Chipiri', 'Chitatu', 'Chinai', 'Chishanu', 'Sabudu'], 'weekdays_short' => ['Dim', 'Pos', 'Pir', 'Tat', 'Nai', 'Sha', 'Sab'], 'weekdays_min' => ['Dim', 'Pos', 'Pir', 'Tat', 'Nai', 'Sha', 'Sab'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sh.php b/vendor/nesbot/carbon/src/Carbon/Lang/sh.php index e03b50675..d65f90ec2 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/sh.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sh.php @@ -9,16 +9,6 @@ * file that was distributed with this source code. */ -// @codeCoverageIgnoreStart -use Symfony\Component\Translation\PluralizationRules; - -if (class_exists('Symfony\\Component\\Translation\\PluralizationRules')) { - PluralizationRules::set(static function ($number) { - return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2); - }, 'sh'); -} -// @codeCoverageIgnoreEnd - /* * Authors: * - Томица Кораћ diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/shn_MM.php b/vendor/nesbot/carbon/src/Carbon/Lang/shn_MM.php index f399acf0d..9eeba47fe 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/shn_MM.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/shn_MM.php @@ -14,6 +14,7 @@ * - ubuntu Myanmar LoCo Team https://ubuntu-mm.net Bone Pyae Sone bone.burma@mail.com */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'formats' => [ 'L' => 'OY MMM OD dddd', ], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/shs_CA.php b/vendor/nesbot/carbon/src/Carbon/Lang/shs_CA.php index 08d385e66..f41c34df6 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/shs_CA.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/shs_CA.php @@ -22,6 +22,7 @@ 'weekdays' => ['Sxetspesq̓t', 'Spetkesq̓t', 'Selesq̓t', 'Skellesq̓t', 'Smesesq̓t', 'Stselkstesq̓t', 'Stqmekstesq̓t'], 'weekdays_short' => ['Sxe', 'Spe', 'Sel', 'Ske', 'Sme', 'Sts', 'Stq'], 'weekdays_min' => ['Sxe', 'Spe', 'Sel', 'Ske', 'Sme', 'Sts', 'Stq'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'year' => ':count sqlélten', // less reliable diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/si.php b/vendor/nesbot/carbon/src/Carbon/Lang/si.php index 636bf6914..7d14ca6c9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/si.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/si.php @@ -32,16 +32,16 @@ 'second' => '{1}තත්පර 1|තත්පර :count', 'a_second' => '{1}තත්පර කිහිපයකට|තත්පර :count', 'ago' => ':time කට පෙර', - 'from_now' => function ($time) { - if (preg_match('/දින \d/u', $time)) { + 'from_now' => static function ($time) { + if (preg_match('/දින \d+/u', $time)) { return $time.' න්'; } return $time.' කින්'; }, 'before' => ':time කට පෙර', - 'after' => function ($time) { - if (preg_match('/දින \d/u', $time)) { + 'after' => static function ($time) { + if (preg_match('/දින \d+/u', $time)) { return $time.' න්'; } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sid_ET.php b/vendor/nesbot/carbon/src/Carbon/Lang/sid_ET.php index 1296f9be7..5e9632d19 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/sid_ET.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sid_ET.php @@ -22,6 +22,7 @@ 'weekdays' => ['Sambata', 'Sanyo', 'Maakisanyo', 'Roowe', 'Hamuse', 'Arbe', 'Qidaame'], 'weekdays_short' => ['Sam', 'San', 'Mak', 'Row', 'Ham', 'Arb', 'Qid'], 'weekdays_min' => ['Sam', 'San', 'Mak', 'Row', 'Ham', 'Arb', 'Qid'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['soodo', 'hawwaro'], ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sk.php b/vendor/nesbot/carbon/src/Carbon/Lang/sk.php index f9702e960..051e93564 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/sk.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sk.php @@ -32,6 +32,7 @@ * - Jakub ADAMEC * - Marek Adamický * - AlterwebStudio + * - Peter Kundis */ use Carbon\CarbonInterface; @@ -56,13 +57,15 @@ ]; $replacementsPlural = [ - '/\bhodiny\b/' => 'hodinami', - '/\bminúty\b/' => 'minútami', - '/\bsekundy\b/' => 'sekundami', + '/\b(?:hodiny|hodín)\b/' => 'hodinami', + '/\b(?:minúty|minút)\b/' => 'minútami', + '/\b(?:sekundy|sekúnd)\b/' => 'sekundami', + '/\bdeň\b/' => 'dňom', '/\bdni\b/' => 'dňami', - '/\btýždne\b/' => 'týždňami', - '/\bmesiace\b/' => 'mesiacmi', - '/\broky\b/' => 'rokmi', + '/\bdní\b/u' => 'dňami', + '/\b(?:týždne|týždňov)\b/' => 'týždňami', + '/\b(?:mesiace|mesiacov)\b/' => 'mesiacmi', + '/\b(?:roky|rokov)\b/' => 'rokmi', ]; foreach ($replacements + $replacementsPlural as $pattern => $replacement) { @@ -149,7 +152,9 @@ 'weekdays' => ['nedeľa', 'pondelok', 'utorok', 'streda', 'štvrtok', 'piatok', 'sobota'], 'weekdays_short' => ['ned', 'pon', 'uto', 'str', 'štv', 'pia', 'sob'], 'weekdays_min' => ['ne', 'po', 'ut', 'st', 'št', 'pi', 'so'], - 'months' => ['január', 'február', 'marec', 'apríl', 'máj', 'jún', 'júl', 'august', 'september', 'október', 'november', 'december'], + 'months' => ['januára', 'februára', 'marca', 'apríla', 'mája', 'júna', 'júla', 'augusta', 'septembra', 'októbra', 'novembra', 'decembra'], + 'months_standalone' => ['január', 'február', 'marec', 'apríl', 'máj', 'jún', 'júl', 'august', 'september', 'október', 'november', 'december'], 'months_short' => ['jan', 'feb', 'mar', 'apr', 'máj', 'jún', 'júl', 'aug', 'sep', 'okt', 'nov', 'dec'], + 'months_regexp' => '/(DD?o?\.?(\[[^\[\]]*\]|\s)+MMMM?|L{2,4}|l{2,4})/', 'meridiem' => ['dopoludnia', 'popoludní'], ]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sl.php b/vendor/nesbot/carbon/src/Carbon/Lang/sl.php index 1f1d1b338..012742e10 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/sl.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sl.php @@ -99,7 +99,7 @@ 'nextDay' => '[jutri ob] LT', 'nextWeek' => 'dddd [ob] LT', 'lastDay' => '[včeraj ob] LT', - 'lastWeek' => function (CarbonInterface $date) { + 'lastWeek' => static function (CarbonInterface $date) { switch ($date->dayOfWeek) { case 0: return '[preteklo] [nedeljo] [ob] LT'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sm_WS.php b/vendor/nesbot/carbon/src/Carbon/Lang/sm_WS.php index f06606871..1568af6ed 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/sm_WS.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sm_WS.php @@ -14,6 +14,7 @@ * - Samsung Electronics Co., Ltd. akhilesh.k@samsung.com */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'formats' => [ 'L' => 'DD/MM/YYYY', ], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sn.php b/vendor/nesbot/carbon/src/Carbon/Lang/sn.php index 4f25028a3..095936fc7 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/sn.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sn.php @@ -10,6 +10,7 @@ */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'meridiem' => ['a', 'p'], 'weekdays' => ['Svondo', 'Muvhuro', 'Chipiri', 'Chitatu', 'China', 'Chishanu', 'Mugovera'], 'weekdays_short' => ['Svo', 'Muv', 'Chp', 'Cht', 'Chn', 'Chs', 'Mug'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/so.php b/vendor/nesbot/carbon/src/Carbon/Lang/so.php index 578527197..78e487cc5 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/so.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/so.php @@ -13,10 +13,10 @@ * Author: * - Abdifatah Abdilahi(@abdifatahz) */ -return [ +return array_replace_recursive(require __DIR__.'/en.php', [ 'year' => ':count sanad|:count sanadood', 'a_year' => 'sanad|:count sanadood', - 'y' => '{1}:countsn|{0}:countsns|]1,Inf[:countsn', + 'y' => '{1}:countsn|{0}:countsns|[-Inf,Inf]:countsn', 'month' => ':count bil|:count bilood', 'a_month' => 'bil|:count bilood', 'm' => ':countbil', @@ -71,4 +71,4 @@ 'lastWeek' => '[Hore] dddd [Markay ahayd] LT', 'sameElse' => 'L', ], -]; +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr.php index 68ba663aa..f1908a22f 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/sr.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr.php @@ -26,7 +26,7 @@ 'year' => ':count godina|:count godine|:count godina', 'y' => ':count g.', 'month' => ':count mesec|:count meseca|:count meseci', - 'm' => ':count mj.', + 'm' => ':count mes.', 'week' => ':count nedelja|:count nedelje|:count nedelja', 'w' => ':count ned.', 'day' => ':count dan|:count dana|:count dana', @@ -37,15 +37,16 @@ 'min' => ':count min.', 'second' => ':count sekundu|:count sekunde|:count sekundi', 's' => ':count sek.', + 'ago' => 'pre :time', 'from_now' => 'za :time', 'after' => 'nakon :time', 'before' => 'pre :time', - 'year_from_now' => ':count godinu|:count godine|:count godina', 'year_ago' => ':count godinu|:count godine|:count godina', - 'week_from_now' => ':count nedelju|:count nedelje|:count nedelja', + 'year_from_now' => ':count godinu|:count godine|:count godina', 'week_ago' => ':count nedelju|:count nedelje|:count nedelja', + 'week_from_now' => ':count nedelju|:count nedelje|:count nedelja', 'diff_now' => 'upravo sada', 'diff_today' => 'danas', @@ -67,36 +68,21 @@ 'calendar' => [ 'sameDay' => '[danas u] LT', 'nextDay' => '[sutra u] LT', - 'nextWeek' => function (CarbonInterface $date) { - switch ($date->dayOfWeek) { - case 0: - return '[u nedelju u] LT'; - case 3: - return '[u sredu u] LT'; - case 6: - return '[u subotu u] LT'; - default: - return '[u] dddd [u] LT'; - } + 'nextWeek' => static fn (CarbonInterface $date) => match ($date->dayOfWeek) { + 0 => '[u nedelju u] LT', + 3 => '[u sredu u] LT', + 6 => '[u subotu u] LT', + default => '[u] dddd [u] LT', }, 'lastDay' => '[juče u] LT', - 'lastWeek' => function (CarbonInterface $date) { - switch ($date->dayOfWeek) { - case 0: - return '[prošle nedelje u] LT'; - case 1: - return '[prošlog ponedeljka u] LT'; - case 2: - return '[prošlog utorka u] LT'; - case 3: - return '[prošle srede u] LT'; - case 4: - return '[prošlog četvrtka u] LT'; - case 5: - return '[prošlog petka u] LT'; - default: - return '[prošle subote u] LT'; - } + 'lastWeek' => static fn (CarbonInterface $date) => match ($date->dayOfWeek) { + 0 => '[prošle nedelje u] LT', + 1 => '[prošlog ponedeljka u] LT', + 2 => '[prošlog utorka u] LT', + 3 => '[prošle srede u] LT', + 4 => '[prošlog četvrtka u] LT', + 5 => '[prošlog petka u] LT', + default => '[prošle subote u] LT', }, 'sameElse' => 'L', ], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl.php index 8becbc576..fe42d5abc 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl.php @@ -66,36 +66,21 @@ 'calendar' => [ 'sameDay' => '[данас у] LT', 'nextDay' => '[сутра у] LT', - 'nextWeek' => function (CarbonInterface $date) { - switch ($date->dayOfWeek) { - case 0: - return '[у недељу у] LT'; - case 3: - return '[у среду у] LT'; - case 6: - return '[у суботу у] LT'; - default: - return '[у] dddd [у] LT'; - } + 'nextWeek' => static fn (CarbonInterface $date) => match ($date->dayOfWeek) { + 0 => '[у недељу у] LT', + 3 => '[у среду у] LT', + 6 => '[у суботу у] LT', + default => '[у] dddd [у] LT', }, 'lastDay' => '[јуче у] LT', - 'lastWeek' => function (CarbonInterface $date) { - switch ($date->dayOfWeek) { - case 0: - return '[прошле недеље у] LT'; - case 1: - return '[прошлог понедељка у] LT'; - case 2: - return '[прошлог уторка у] LT'; - case 3: - return '[прошле среде у] LT'; - case 4: - return '[прошлог четвртка у] LT'; - case 5: - return '[прошлог петка у] LT'; - default: - return '[прошле суботе у] LT'; - } + 'lastWeek' => static fn (CarbonInterface $date) => match ($date->dayOfWeek) { + 0 => '[прошле недеље у] LT', + 1 => '[прошлог понедељка у] LT', + 2 => '[прошлог уторка у] LT', + 3 => '[прошле среде у] LT', + 4 => '[прошлог четвртка у] LT', + 5 => '[прошлог петка у] LT', + default => '[прошле суботе у] LT', }, 'sameElse' => 'L', ], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_ME.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_ME.php index 28d22fd2c..e34f732a1 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_ME.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_ME.php @@ -72,36 +72,21 @@ 'calendar' => [ 'sameDay' => '[данас у] LT', 'nextDay' => '[сутра у] LT', - 'nextWeek' => function (CarbonInterface $date) { - switch ($date->dayOfWeek) { - case 0: - return '[у недељу у] LT'; - case 3: - return '[у среду у] LT'; - case 6: - return '[у суботу у] LT'; - default: - return '[у] dddd [у] LT'; - } + 'nextWeek' => static fn (CarbonInterface $date) => match ($date->dayOfWeek) { + 0 => '[у недељу у] LT', + 3 => '[у среду у] LT', + 6 => '[у суботу у] LT', + default => '[у] dddd [у] LT', }, 'lastDay' => '[јуче у] LT', - 'lastWeek' => function (CarbonInterface $date) { - switch ($date->dayOfWeek) { - case 0: - return '[прошле недеље у] LT'; - case 1: - return '[прошлог понедељка у] LT'; - case 2: - return '[прошлог уторка у] LT'; - case 3: - return '[прошле среде у] LT'; - case 4: - return '[прошлог четвртка у] LT'; - case 5: - return '[прошлог петка у] LT'; - default: - return '[прошле суботе у] LT'; - } + 'lastWeek' => static fn (CarbonInterface $date) => match ($date->dayOfWeek) { + 0 => '[прошле недеље у] LT', + 1 => '[прошлог понедељка у] LT', + 2 => '[прошлог уторка у] LT', + 3 => '[прошле среде у] LT', + 4 => '[прошлог четвртка у] LT', + 5 => '[прошлог петка у] LT', + default => '[прошле суботе у] LT', }, 'sameElse' => 'L', ], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_ME.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_ME.php index 5b8f2d062..811508018 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_ME.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_ME.php @@ -40,35 +40,20 @@ 'diff_tomorrow' => 'sjutra', 'calendar' => [ 'nextDay' => '[sjutra u] LT', - 'nextWeek' => function (CarbonInterface $date) { - switch ($date->dayOfWeek) { - case 0: - return '[u nedjelju u] LT'; - case 3: - return '[u srijedu u] LT'; - case 6: - return '[u subotu u] LT'; - default: - return '[u] dddd [u] LT'; - } + 'nextWeek' => static fn (CarbonInterface $date) => match ($date->dayOfWeek) { + 0 => '[u nedjelju u] LT', + 3 => '[u srijedu u] LT', + 6 => '[u subotu u] LT', + default => '[u] dddd [u] LT', }, - 'lastWeek' => function (CarbonInterface $date) { - switch ($date->dayOfWeek) { - case 0: - return '[prošle nedjelje u] LT'; - case 1: - return '[prošle nedjelje u] LT'; - case 2: - return '[prošlog utorka u] LT'; - case 3: - return '[prošle srijede u] LT'; - case 4: - return '[prošlog četvrtka u] LT'; - case 5: - return '[prošlog petka u] LT'; - default: - return '[prošle subote u] LT'; - } + 'lastWeek' => static fn (CarbonInterface $date) => match ($date->dayOfWeek) { + 0 => '[prošle nedjelje u] LT', + 1 => '[prošle nedjelje u] LT', + 2 => '[prošlog utorka u] LT', + 3 => '[prošle srijede u] LT', + 4 => '[prošlog četvrtka u] LT', + 5 => '[prošlog petka u] LT', + default => '[prošle subote u] LT', }, ], 'weekdays' => ['nedjelja', 'ponedjeljak', 'utorak', 'srijeda', 'četvrtak', 'petak', 'subota'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ss.php b/vendor/nesbot/carbon/src/Carbon/Lang/ss.php index 1c52c9bf6..0ec3e8f99 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ss.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ss.php @@ -46,7 +46,7 @@ 'lastWeek' => 'dddd [leliphelile] [nga] LT', 'sameElse' => 'L', ], - 'ordinal' => function ($number) { + 'ordinal' => static function ($number) { $lastDigit = $number % 10; return $number.( @@ -55,7 +55,7 @@ ) ); }, - 'meridiem' => function ($hour) { + 'meridiem' => static function ($hour) { if ($hour < 11) { return 'ekuseni'; } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/st_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/st_ZA.php index 5bce7f20f..5eee2224c 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/st_ZA.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/st_ZA.php @@ -22,6 +22,7 @@ 'weekdays' => ['Sontaha', 'Mantaha', 'Labobedi', 'Laboraro', 'Labone', 'Labohlano', 'Moqebelo'], 'weekdays_short' => ['Son', 'Mma', 'Bed', 'Rar', 'Ne', 'Hla', 'Moq'], 'weekdays_min' => ['Son', 'Mma', 'Bed', 'Rar', 'Ne', 'Hla', 'Moq'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'week' => ':count Sontaha', // less reliable diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sv.php b/vendor/nesbot/carbon/src/Carbon/Lang/sv.php index 1706c7191..d7e0ddf28 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/sv.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sv.php @@ -66,7 +66,7 @@ 'lastWeek' => '[I] dddd[s] LT', 'sameElse' => 'L', ], - 'ordinal' => function ($number) { + 'ordinal' => static function ($number) { $lastDigit = $number % 10; return $number.( diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ta.php b/vendor/nesbot/carbon/src/Carbon/Lang/ta.php index c1d89cbb3..c5dd68e79 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ta.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ta.php @@ -63,7 +63,7 @@ 'sameElse' => 'L', ], 'ordinal' => ':numberவது', - 'meridiem' => function ($hour) { + 'meridiem' => static function ($hour) { if ($hour < 2) { return ' யாமம்'; } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tcy_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/tcy_IN.php index 2ff20e0e6..f2bbf1037 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/tcy_IN.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tcy_IN.php @@ -22,6 +22,7 @@ 'weekdays' => ['ಐಥಾರ', 'ಸೋಮಾರ', 'ಅಂಗರೆ', 'ಬುಧಾರ', 'ಗುರುವಾರ', 'ಶುಕ್ರರ', 'ಶನಿವಾರ'], 'weekdays_short' => ['ಐ', 'ಸೋ', 'ಅಂ', 'ಬು', 'ಗು', 'ಶು', 'ಶ'], 'weekdays_min' => ['ಐ', 'ಸೋ', 'ಅಂ', 'ಬು', 'ಗು', 'ಶು', 'ಶ'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['ಕಾಂಡೆ', 'ಬಯ್ಯ'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/te.php b/vendor/nesbot/carbon/src/Carbon/Lang/te.php index ac38218f3..52d4809e9 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/te.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/te.php @@ -61,7 +61,7 @@ 'sameElse' => 'L', ], 'ordinal' => ':numberవ', - 'meridiem' => function ($hour) { + 'meridiem' => static function ($hour) { if ($hour < 4) { return 'రాత్రి'; } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tg.php b/vendor/nesbot/carbon/src/Carbon/Lang/tg.php index b7df893c1..57b451352 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/tg.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tg.php @@ -45,7 +45,7 @@ 'lastWeek' => 'dddd[и] [ҳафтаи гузашта соати] LT', 'sameElse' => 'L', ], - 'ordinal' => function ($number) { + 'ordinal' => static function ($number) { if ($number === 0) { // special case for zero return "$number-ıncı"; } @@ -77,7 +77,7 @@ return $number.($suffixes[$number] ?? $suffixes[$number % 10] ?? $suffixes[$number >= 100 ? 100 : -1] ?? ''); }, - 'meridiem' => function ($hour) { + 'meridiem' => static function ($hour) { if ($hour < 4) { return 'шаб'; } diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/th.php b/vendor/nesbot/carbon/src/Carbon/Lang/th.php index 6397f6e46..78980d5c2 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/th.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/th.php @@ -34,7 +34,7 @@ 'minute' => ':count นาที', 'min' => ':count นาที', 'second' => ':count วินาที', - 'a_second' => '{1}ไม่กี่วินาที|]1,Inf[:count วินาที', + 'a_second' => '{1}ไม่กี่วินาที|[-Inf,Inf]:count วินาที', 's' => ':count วินาที', 'ago' => ':timeที่แล้ว', 'from_now' => 'อีก :time', diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/the_NP.php b/vendor/nesbot/carbon/src/Carbon/Lang/the_NP.php index 34da16278..cdb02b2ca 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/the_NP.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/the_NP.php @@ -22,6 +22,7 @@ 'weekdays' => ['आइतबार', 'सोमबार', 'मंगलबार', 'बुधबार', 'बिहिबार', 'शुक्रबार', 'शनिबार'], 'weekdays_short' => ['आइत', 'सोम', 'मंगल', 'बुध', 'बिहि', 'शुक्र', 'शनि'], 'weekdays_min' => ['आइत', 'सोम', 'मंगल', 'बुध', 'बिहि', 'शुक्र', 'शनि'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['पूर्वाह्न', 'अपराह्न'], ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ti_ET.php b/vendor/nesbot/carbon/src/Carbon/Lang/ti_ET.php index 024217f2f..a8160698a 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ti_ET.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ti_ET.php @@ -22,6 +22,7 @@ 'weekdays' => ['ሰንበት', 'ሰኑይ', 'ሰሉስ', 'ረቡዕ', 'ሓሙስ', 'ዓርቢ', 'ቀዳም'], 'weekdays_short' => ['ሰንበ', 'ሰኑይ', 'ሰሉስ', 'ረቡዕ', 'ሓሙስ', 'ዓርቢ', 'ቀዳም'], 'weekdays_min' => ['ሰንበ', 'ሰኑይ', 'ሰሉስ', 'ረቡዕ', 'ሓሙስ', 'ዓርቢ', 'ቀዳም'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['ንጉሆ ሰዓተ', 'ድሕር ሰዓት'], ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tk_TM.php b/vendor/nesbot/carbon/src/Carbon/Lang/tk_TM.php index f949a4300..b1c487a07 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/tk_TM.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tk_TM.php @@ -15,12 +15,10 @@ * - SuperManPHP * - Maksat Meredow (isadma) */ -$transformDiff = function ($input) { - return strtr($input, [ - 'sekunt' => 'sekunt', - 'hepde' => 'hepde', - ]); -}; +$transformDiff = static fn (string $input) => strtr($input, [ + 'sekunt' => 'sekunt', + 'hepde' => 'hepde', +]); return array_replace_recursive(require __DIR__.'/en.php', [ 'formats' => [ @@ -28,9 +26,9 @@ ], 'months' => ['Ýanwar', 'Fewral', 'Mart', 'Aprel', 'Maý', 'Iýun', 'Iýul', 'Awgust', 'Sentýabr', 'Oktýabr', 'Noýabr', 'Dekabr'], 'months_short' => ['Ýan', 'Few', 'Mar', 'Apr', 'Maý', 'Iýn', 'Iýl', 'Awg', 'Sen', 'Okt', 'Noý', 'Dek'], - 'weekdays' => ['Duşenbe', 'Sişenbe', 'Çarşenbe', 'Penşenbe', 'Anna', 'Şenbe', 'Ýekşenbe'], - 'weekdays_short' => ['Duş', 'Siş', 'Çar', 'Pen', 'Ann', 'Şen', 'Ýek'], - 'weekdays_min' => ['Du', 'Si', 'Ça', 'Pe', 'An', 'Şe', 'Ýe'], + 'weekdays' => ['Ýekşenbe', 'Duşenbe', 'Sişenbe', 'Çarşenbe', 'Penşenbe', 'Anna', 'Şenbe'], + 'weekdays_short' => ['Ýek', 'Duş', 'Siş', 'Çar', 'Pen', 'Ann', 'Şen'], + 'weekdays_min' => ['Ýe', 'Du', 'Si', 'Ça', 'Pe', 'An', 'Şe'], 'first_day_of_week' => 1, 'day_of_first_week_of_year' => 1, @@ -62,16 +60,8 @@ 's' => ':count sekunt', 'a_second' => ':count sekunt', - 'ago' => function ($time) use ($transformDiff) { - return $transformDiff($time).' ozal'; - }, - 'from_now' => function ($time) use ($transformDiff) { - return $transformDiff($time).' soňra'; - }, - 'after' => function ($time) use ($transformDiff) { - return $transformDiff($time).' soň'; - }, - 'before' => function ($time) use ($transformDiff) { - return $transformDiff($time).' öň'; - }, + 'ago' => static fn (string $time) => $transformDiff($time).' ozal', + 'from_now' => static fn (string $time) => $transformDiff($time).' soňra', + 'after' => static fn (string $time) => $transformDiff($time).' soň', + 'before' => static fn (string $time) => $transformDiff($time).' öň', ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tlh.php b/vendor/nesbot/carbon/src/Carbon/Lang/tlh.php index fbf9e6f72..4ecf2446c 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/tlh.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tlh.php @@ -23,7 +23,7 @@ 'hour' => '{1}wa’ rep|:count rep', 'minute' => '{1}wa’ tup|:count tup', 'second' => '{1}puS lup|:count lup', - 'ago' => function ($time) { + 'ago' => static function ($time) { $output = strtr($time, [ 'jaj' => 'Hu’', 'jar' => 'wen', @@ -32,7 +32,7 @@ return $output === $time ? "$time ret" : $output; }, - 'from_now' => function ($time) { + 'from_now' => static function ($time) { $output = strtr($time, [ 'jaj' => 'leS', 'jar' => 'waQ', diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tn_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/tn_ZA.php index aada7db58..e3df8f68e 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/tn_ZA.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tn_ZA.php @@ -22,6 +22,7 @@ 'weekdays' => ['laTshipi', 'Mosupologo', 'Labobedi', 'Laboraro', 'Labone', 'Labotlhano', 'Lamatlhatso'], 'weekdays_short' => ['Tsh', 'Mos', 'Bed', 'Rar', 'Ne', 'Tlh', 'Mat'], 'weekdays_min' => ['Tsh', 'Mos', 'Bed', 'Rar', 'Ne', 'Tlh', 'Mat'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'year' => 'dingwaga di le :count', diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/to_TO.php b/vendor/nesbot/carbon/src/Carbon/Lang/to_TO.php index 335c69a85..ce713edba 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/to_TO.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/to_TO.php @@ -14,6 +14,7 @@ * - International Components for Unicode akhilesh.k@samsung.com */ return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 0, 'formats' => [ 'L' => 'dddd DD MMM YYYY', ], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tpi_PG.php b/vendor/nesbot/carbon/src/Carbon/Lang/tpi_PG.php index 5f58c44c0..721b625b5 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/tpi_PG.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tpi_PG.php @@ -22,6 +22,7 @@ 'weekdays' => ['Sande', 'Mande', 'Tunde', 'Trinde', 'Fonde', 'Fraide', 'Sarere'], 'weekdays_short' => ['San', 'Man', 'Tun', 'Tri', 'Fon', 'Fra', 'Sar'], 'weekdays_min' => ['San', 'Man', 'Tun', 'Tri', 'Fon', 'Fra', 'Sar'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['biknait', 'apinun'], diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tr.php b/vendor/nesbot/carbon/src/Carbon/Lang/tr.php index f5d9f4cc5..0cb3aade4 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/tr.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tr.php @@ -23,25 +23,25 @@ */ return [ 'year' => ':count yıl', - 'a_year' => '{1}bir yıl|]1,Inf[:count yıl', + 'a_year' => '{1}bir yıl|[-Inf,Inf]:count yıl', 'y' => ':county', 'month' => ':count ay', - 'a_month' => '{1}bir ay|]1,Inf[:count ay', + 'a_month' => '{1}bir ay|[-Inf,Inf]:count ay', 'm' => ':countay', 'week' => ':count hafta', - 'a_week' => '{1}bir hafta|]1,Inf[:count hafta', + 'a_week' => '{1}bir hafta|[-Inf,Inf]:count hafta', 'w' => ':counth', 'day' => ':count gün', - 'a_day' => '{1}bir gün|]1,Inf[:count gün', + 'a_day' => '{1}bir gün|[-Inf,Inf]:count gün', 'd' => ':countg', 'hour' => ':count saat', - 'a_hour' => '{1}bir saat|]1,Inf[:count saat', + 'a_hour' => '{1}bir saat|[-Inf,Inf]:count saat', 'h' => ':countsa', 'minute' => ':count dakika', - 'a_minute' => '{1}bir dakika|]1,Inf[:count dakika', + 'a_minute' => '{1}bir dakika|[-Inf,Inf]:count dakika', 'min' => ':countdk', 'second' => ':count saniye', - 'a_second' => '{1}birkaç saniye|]1,Inf[:count saniye', + 'a_second' => '{1}birkaç saniye|[-Inf,Inf]:count saniye', 's' => ':countsn', 'ago' => ':time önce', 'from_now' => ':time sonra', @@ -71,7 +71,7 @@ 'lastWeek' => '[geçen] dddd [saat] LT', 'sameElse' => 'L', ], - 'ordinal' => function ($number, $period) { + 'ordinal' => static function ($number, $period) { switch ($period) { case 'd': case 'D': diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ts_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/ts_ZA.php index 37a24ec49..327134551 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ts_ZA.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ts_ZA.php @@ -22,6 +22,7 @@ 'weekdays' => ['Sonto', 'Musumbhunuku', 'Ravumbirhi', 'Ravunharhu', 'Ravumune', 'Ravuntlhanu', 'Mugqivela'], 'weekdays_short' => ['Son', 'Mus', 'Bir', 'Har', 'Ne', 'Tlh', 'Mug'], 'weekdays_min' => ['Son', 'Mus', 'Bir', 'Har', 'Ne', 'Tlh', 'Mug'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'year' => 'malembe ya :count', diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ug.php b/vendor/nesbot/carbon/src/Carbon/Lang/ug.php index 259b99a48..12c9f8a67 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ug.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ug.php @@ -46,20 +46,14 @@ 'lastWeek' => '[ئالدىنقى] dddd [سائەت] LT', 'sameElse' => 'L', ], - 'ordinal' => function ($number, $period) { - switch ($period) { - case 'd': - case 'D': - case 'DDD': - return $number.'-كۈنى'; - case 'w': - case 'W': - return $number.'-ھەپتە'; - default: - return $number; - } + 'ordinal' => static function ($number, $period) { + return match ($period) { + 'd', 'D', 'DDD' => $number.'-كۈنى', + 'w', 'W' => $number.'-ھەپتە', + default => $number, + }; }, - 'meridiem' => function ($hour, $minute) { + 'meridiem' => static function ($hour, $minute) { $time = $hour * 100 + $minute; if ($time < 600) { return 'يېرىم كېچە'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/uk.php b/vendor/nesbot/carbon/src/Carbon/Lang/uk.php index 4217d16ed..3bedd2ea2 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/uk.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/uk.php @@ -11,7 +11,7 @@ use Carbon\CarbonInterface; -$processHoursFunction = function (CarbonInterface $date, string $format) { +$processHoursFunction = static function (CarbonInterface $date, string $format) { return $format.'о'.($date->hour === 11 ? 'б' : '').'] LT'; }; @@ -130,52 +130,30 @@ 'LLLL' => 'dddd, D MMMM YYYY, HH:mm', ], 'calendar' => [ - 'sameDay' => function (CarbonInterface $date) use ($processHoursFunction) { - return $processHoursFunction($date, '[Сьогодні '); - }, - 'nextDay' => function (CarbonInterface $date) use ($processHoursFunction) { - return $processHoursFunction($date, '[Завтра '); - }, - 'nextWeek' => function (CarbonInterface $date) use ($processHoursFunction) { - return $processHoursFunction($date, '[У] dddd ['); - }, - 'lastDay' => function (CarbonInterface $date) use ($processHoursFunction) { - return $processHoursFunction($date, '[Вчора '); - }, - 'lastWeek' => function (CarbonInterface $date) use ($processHoursFunction) { - switch ($date->dayOfWeek) { - case 0: - case 3: - case 5: - case 6: - return $processHoursFunction($date, '[Минулої] dddd ['); - default: - return $processHoursFunction($date, '[Минулого] dddd ['); - } + 'sameDay' => static fn (CarbonInterface $date) => $processHoursFunction($date, '[Сьогодні '), + 'nextDay' => static fn (CarbonInterface $date) => $processHoursFunction($date, '[Завтра '), + 'nextWeek' => static fn (CarbonInterface $date) => $processHoursFunction($date, '[У] dddd ['), + 'lastDay' => static fn (CarbonInterface $date) => $processHoursFunction($date, '[Вчора '), + 'lastWeek' => static fn (CarbonInterface $date) => match ($date->dayOfWeek) { + 0, 3, 5, 6 => $processHoursFunction($date, '[Минулої] dddd ['), + default => $processHoursFunction($date, '[Минулого] dddd ['), }, 'sameElse' => 'L', ], - 'ordinal' => function ($number, $period) { - switch ($period) { - case 'M': - case 'd': - case 'DDD': - case 'w': - case 'W': - return $number.'-й'; - case 'D': - return $number.'-го'; - default: - return $number; - } + 'ordinal' => static fn ($number, $period) => match ($period) { + 'M', 'd', 'DDD', 'w', 'W' => $number.'-й', + 'D' => $number.'-го', + default => $number, }, - 'meridiem' => function ($hour) { + 'meridiem' => static function ($hour) { if ($hour < 4) { return 'ночі'; } + if ($hour < 12) { return 'ранку'; } + if ($hour < 17) { return 'дня'; } @@ -186,14 +164,14 @@ 'months_standalone' => ['січень', 'лютий', 'березень', 'квітень', 'травень', 'червень', 'липень', 'серпень', 'вересень', 'жовтень', 'листопад', 'грудень'], 'months_short' => ['січ', 'лют', 'бер', 'кві', 'тра', 'чер', 'лип', 'сер', 'вер', 'жов', 'лис', 'гру'], 'months_regexp' => '/(D[oD]?(\[[^\[\]]*\]|\s)+MMMM?|L{2,4}|l{2,4})/', - 'weekdays' => function (CarbonInterface $date, $format, $index) { + 'weekdays' => static function (CarbonInterface $date, $format, $index) { static $words = [ 'nominative' => ['неділя', 'понеділок', 'вівторок', 'середа', 'четвер', 'п’ятниця', 'субота'], 'accusative' => ['неділю', 'понеділок', 'вівторок', 'середу', 'четвер', 'п’ятницю', 'суботу'], 'genitive' => ['неділі', 'понеділка', 'вівторка', 'середи', 'четверга', 'п’ятниці', 'суботи'], ]; - $format = $format ?? ''; + $format ??= ''; $nounCase = preg_match('/(\[(В|в|У|у)\])\s+dddd/u', $format) ? 'accusative' : ( diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/unm_US.php b/vendor/nesbot/carbon/src/Carbon/Lang/unm_US.php index fa5c374e3..161a1ec88 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/unm_US.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/unm_US.php @@ -22,6 +22,7 @@ 'weekdays' => ['kentuwei', 'manteke', 'tusteke', 'lelai', 'tasteke', 'pelaiteke', 'sateteke'], 'weekdays_short' => ['ken', 'man', 'tus', 'lel', 'tas', 'pel', 'sat'], 'weekdays_min' => ['ken', 'man', 'tus', 'lel', 'tas', 'pel', 'sat'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, // Too unreliable diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ur.php b/vendor/nesbot/carbon/src/Carbon/Lang/ur.php index dc16c2c35..ac960f3f4 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ur.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ur.php @@ -46,15 +46,22 @@ * - hafezdivandari * - Hossein Jabbari * - nimamo + * - Usman Zahid */ return [ - 'year' => 'ایک سال|:count سال', - 'month' => 'ایک ماہ|:count ماہ', - 'week' => ':count ہفتے', - 'day' => 'ایک دن|:count دن', - 'hour' => 'ایک گھنٹہ|:count گھنٹے', - 'minute' => 'ایک منٹ|:count منٹ', - 'second' => 'چند سیکنڈ|:count سیکنڈ', + 'year' => ':count '.'سال', + 'a_year' => 'ایک سال|:count سال', + 'month' => ':count '.'ماہ', + 'a_month' => 'ایک ماہ|:count ماہ', + 'week' => ':count '.'ہفتے', + 'day' => ':count '.'دن', + 'a_day' => 'ایک دن|:count دن', + 'hour' => ':count '.'گھنٹے', + 'a_hour' => 'ایک گھنٹہ|:count گھنٹے', + 'minute' => ':count '.'منٹ', + 'a_minute' => 'ایک منٹ|:count منٹ', + 'second' => ':count '.'سیکنڈ', + 'a_second' => 'چند سیکنڈ|:count سیکنڈ', 'ago' => ':time قبل', 'from_now' => ':time بعد', 'after' => ':time بعد', diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ve_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/ve_ZA.php index 5eb2b9128..d401d9f24 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/ve_ZA.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ve_ZA.php @@ -22,6 +22,7 @@ 'weekdays' => ['Swondaha', 'Musumbuluwo', 'Ḽavhuvhili', 'Ḽavhuraru', 'Ḽavhuṋa', 'Ḽavhuṱanu', 'Mugivhela'], 'weekdays_short' => ['Swo', 'Mus', 'Vhi', 'Rar', 'ṋa', 'Ṱan', 'Mug'], 'weekdays_min' => ['Swo', 'Mus', 'Vhi', 'Rar', 'ṋa', 'Ṱan', 'Mug'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, // Too unreliable diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/wal_ET.php b/vendor/nesbot/carbon/src/Carbon/Lang/wal_ET.php index a4e619a87..e862f2c59 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/wal_ET.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/wal_ET.php @@ -22,6 +22,7 @@ 'weekdays' => ['ወጋ', 'ሳይኖ', 'ማቆሳኛ', 'አሩዋ', 'ሃሙሳ', 'አርባ', 'ቄራ'], 'weekdays_short' => ['ወጋ ', 'ሳይኖ', 'ማቆሳ', 'አሩዋ', 'ሃሙሳ', 'አርባ', 'ቄራ '], 'weekdays_min' => ['ወጋ ', 'ሳይኖ', 'ማቆሳ', 'አሩዋ', 'ሃሙሳ', 'አርባ', 'ቄራ '], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'meridiem' => ['ማለዶ', 'ቃማ'], ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/xh_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/xh_ZA.php index 910f8311e..009153c7b 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/xh_ZA.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/xh_ZA.php @@ -22,6 +22,7 @@ 'weekdays' => ['iCawa', 'uMvulo', 'lwesiBini', 'lwesiThathu', 'ulweSine', 'lwesiHlanu', 'uMgqibelo'], 'weekdays_short' => ['Caw', 'Mvu', 'Bin', 'Tha', 'Sin', 'Hla', 'Mgq'], 'weekdays_min' => ['Caw', 'Mvu', 'Bin', 'Tha', 'Sin', 'Hla', 'Mgq'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'year' => ':count ihlobo', // less reliable diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yi_US.php b/vendor/nesbot/carbon/src/Carbon/Lang/yi_US.php index f764d36f6..f2dec331d 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/yi_US.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/yi_US.php @@ -22,6 +22,7 @@ 'weekdays' => ['זונטיק', 'מאָנטיק', 'דינסטיק', 'מיטװאָך', 'דאָנערשטיק', 'פֿרײַטיק', 'שבת'], 'weekdays_short' => ['זונ\'', 'מאָנ\'', 'דינ\'', 'מיט\'', 'דאָנ\'', 'פֿרײַ\'', 'שבת'], 'weekdays_min' => ['זונ\'', 'מאָנ\'', 'דינ\'', 'מיט\'', 'דאָנ\'', 'פֿרײַ\'', 'שבת'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'year' => ':count יאר', diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yuw_PG.php b/vendor/nesbot/carbon/src/Carbon/Lang/yuw_PG.php index b99ad2e8e..8a1ccf981 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/yuw_PG.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/yuw_PG.php @@ -22,5 +22,6 @@ 'weekdays' => ['sönda', 'mönda', 'sinda', 'mitiwö', 'sogipbono', 'nenggo', 'söndanggie'], 'weekdays_short' => ['sön', 'mön', 'sin', 'mit', 'soi', 'nen', 'sab'], 'weekdays_min' => ['sön', 'mön', 'sin', 'mit', 'soi', 'nen', 'sab'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, ]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans.php index 9b91785e1..8d8d9d771 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans.php @@ -37,7 +37,7 @@ 'minute' => ':count:optional-space分钟', 'min' => ':count:optional-space分钟', 'second' => ':count:optional-space秒', - 'a_second' => '{1}几秒|]1,Inf[:count:optional-space秒', + 'a_second' => '{1}几秒|[-Inf,Inf]:count:optional-space秒', 's' => ':count:optional-space秒', 'ago' => ':time前', 'from_now' => ':time后', @@ -63,22 +63,15 @@ 'lastWeek' => '[上]ddddLT', 'sameElse' => 'L', ], - 'ordinal' => function ($number, $period) { - switch ($period) { - case 'd': - case 'D': - case 'DDD': - return $number.'日'; - case 'M': - return $number.'月'; - case 'w': - case 'W': - return $number.'周'; - default: - return $number; - } + 'ordinal' => static function ($number, $period) { + return match ($period) { + 'd', 'D', 'DDD' => $number.'日', + 'M' => $number.'月', + 'w', 'W' => $number.'周', + default => $number, + }; }, - 'meridiem' => function ($hour, $minute) { + 'meridiem' => static function ($hour, $minute) { $time = $hour * 100 + $minute; if ($time < 600) { return '凌晨'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant.php index a27b61093..e34db01c1 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant.php @@ -39,7 +39,7 @@ 'minute' => ':count:optional-space分鐘', 'min' => ':count:optional-space分鐘', 'second' => ':count:optional-space秒', - 'a_second' => '{1}幾秒|]1,Inf[:count:optional-space秒', + 'a_second' => '{1}幾秒|[-Inf,Inf]:count:optional-space秒', 's' => ':count:optional-space秒', 'ago' => ':time前', 'from_now' => ':time後', @@ -65,22 +65,15 @@ 'lastWeek' => '[上]dddd LT', 'sameElse' => 'L', ], - 'ordinal' => function ($number, $period) { - switch ($period) { - case 'd': - case 'D': - case 'DDD': - return $number.'日'; - case 'M': - return $number.'月'; - case 'w': - case 'W': - return $number.'周'; - default: - return $number; - } + 'ordinal' => static function ($number, $period) { + return match ($period) { + 'd', 'D', 'DDD' => $number.'日', + 'M' => $number.'月', + 'w', 'W' => $number.'周', + default => $number, + }; }, - 'meridiem' => function ($hour, $minute) { + 'meridiem' => static function ($hour, $minute) { $time = $hour * 100 + $minute; if ($time < 600) { return '凌晨'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zu_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/zu_ZA.php index 6bfb72f0a..b1e8bc0b5 100644 --- a/vendor/nesbot/carbon/src/Carbon/Lang/zu_ZA.php +++ b/vendor/nesbot/carbon/src/Carbon/Lang/zu_ZA.php @@ -22,6 +22,7 @@ 'weekdays' => ['iSonto', 'uMsombuluko', 'uLwesibili', 'uLwesithathu', 'uLwesine', 'uLwesihlanu', 'uMgqibelo'], 'weekdays_short' => ['Son', 'Mso', 'Bil', 'Tha', 'Sin', 'Hla', 'Mgq'], 'weekdays_min' => ['Son', 'Mso', 'Bil', 'Tha', 'Sin', 'Hla', 'Mgq'], + 'first_day_of_week' => 0, 'day_of_first_week_of_year' => 1, 'year' => 'kweminyaka engu-:count', diff --git a/vendor/nesbot/carbon/src/Carbon/Language.php b/vendor/nesbot/carbon/src/Carbon/Language.php index 1fb5bafdc..6197e8b00 100644 --- a/vendor/nesbot/carbon/src/Carbon/Language.php +++ b/vendor/nesbot/carbon/src/Carbon/Language.php @@ -1,5 +1,7 @@ names) { - $this->names = static::all()[$this->code] ?? [ - 'isoName' => $this->code, - 'nativeName' => $this->code, - ]; - } + $this->names ??= static::all()[$this->code] ?? [ + 'isoName' => $this->code, + 'nativeName' => $this->code, + ]; return $this->names; } /** * Returns the original locale ID. - * - * @return string */ public function getId(): string { @@ -135,8 +100,6 @@ public function getId(): string /** * Returns the code of the locale "en"/"fr". - * - * @return string */ public function getCode(): string { @@ -145,8 +108,6 @@ public function getCode(): string /** * Returns the variant code such as cyrl/latn. - * - * @return string|null */ public function getVariant(): ?string { @@ -155,8 +116,6 @@ public function getVariant(): ?string /** * Returns the variant such as Cyrillic/Latin. - * - * @return string|null */ public function getVariantName(): ?string { @@ -173,8 +132,6 @@ public function getVariantName(): ?string /** * Returns the region part of the locale. - * - * @return string|null */ public function getRegion(): ?string { @@ -184,7 +141,8 @@ public function getRegion(): ?string /** * Returns the region name for the current language. * - * @return string|null + * ⚠ ISO 3166-2 short name provided with no warranty, should not + * be used for any purpose to show official state names. */ public function getRegionName(): ?string { @@ -193,24 +151,18 @@ public function getRegionName(): ?string /** * Returns the long ISO language name. - * - * @return string */ public function getFullIsoName(): string { - if (!$this->isoName) { - $this->isoName = $this->getNames()['isoName']; - } + $this->isoName ??= $this->getNames()['isoName']; return $this->isoName; } /** * Set the ISO language name. - * - * @param string $isoName */ - public function setIsoName(string $isoName): self + public function setIsoName(string $isoName): static { $this->isoName = $isoName; @@ -219,24 +171,18 @@ public function setIsoName(string $isoName): self /** * Return the full name of the language in this language. - * - * @return string */ public function getFullNativeName(): string { - if (!$this->nativeName) { - $this->nativeName = $this->getNames()['nativeName']; - } + $this->nativeName ??= $this->getNames()['nativeName']; return $this->nativeName; } /** * Set the name of the language in this language. - * - * @param string $nativeName */ - public function setNativeName(string $nativeName): self + public function setNativeName(string $nativeName): static { $this->nativeName = $nativeName; @@ -245,8 +191,6 @@ public function setNativeName(string $nativeName): self /** * Returns the short ISO language name. - * - * @return string */ public function getIsoName(): string { @@ -257,8 +201,6 @@ public function getIsoName(): string /** * Get the short name of the language in this language. - * - * @return string */ public function getNativeName(): string { @@ -269,10 +211,8 @@ public function getNativeName(): string /** * Get a string with short ISO name, region in parentheses if applicable, variant in parentheses if applicable. - * - * @return string */ - public function getIsoDescription() + public function getIsoDescription(): string { $region = $this->getRegionName(); $variant = $this->getVariantName(); @@ -282,10 +222,8 @@ public function getIsoDescription() /** * Get a string with short native name, region in parentheses if applicable, variant in parentheses if applicable. - * - * @return string */ - public function getNativeDescription() + public function getNativeDescription(): string { $region = $this->getRegionName(); $variant = $this->getVariantName(); @@ -295,10 +233,8 @@ public function getNativeDescription() /** * Get a string with long ISO name, region in parentheses if applicable, variant in parentheses if applicable. - * - * @return string */ - public function getFullIsoDescription() + public function getFullIsoDescription(): string { $region = $this->getRegionName(); $variant = $this->getVariantName(); @@ -308,10 +244,8 @@ public function getFullIsoDescription() /** * Get a string with long native name, region in parentheses if applicable, variant in parentheses if applicable. - * - * @return string */ - public function getFullNativeDescription() + public function getFullNativeDescription(): string { $region = $this->getRegionName(); $variant = $this->getVariantName(); @@ -321,21 +255,16 @@ public function getFullNativeDescription() /** * Returns the original locale ID. - * - * @return string */ - public function __toString() + public function __toString(): string { return $this->getId(); } /** * Get a string with short ISO name, region in parentheses if applicable, variant in parentheses if applicable. - * - * @return string */ - #[ReturnTypeWillChange] - public function jsonSerialize() + public function jsonSerialize(): string { return $this->getIsoDescription(); } diff --git a/vendor/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php b/vendor/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php index 84e241e3e..76fc24a15 100644 --- a/vendor/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php +++ b/vendor/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php @@ -1,5 +1,7 @@ appGetter = $appGetter; @@ -40,9 +45,15 @@ public function setLocaleGetter(?callable $localeGetter): void $this->localeGetter = $localeGetter; } + public function setFallbackLocaleGetter(?callable $fallbackLocaleGetter): void + { + $this->fallbackLocaleGetter = $fallbackLocaleGetter; + } + public function boot() { $this->updateLocale(); + $this->updateFallbackLocale(); if (!$this->app->bound('events')) { return; @@ -79,7 +90,34 @@ public function updateLocale() try { $root = Date::getFacadeRoot(); $root->setLocale($locale); - } catch (Throwable $e) { + } catch (Throwable) { + // Non Carbon class in use in Date facade + } + } + } + + public function updateFallbackLocale() + { + $locale = $this->getFallbackLocale(); + + if ($locale === null) { + return; + } + + Carbon::setFallbackLocale($locale); + CarbonImmutable::setFallbackLocale($locale); + CarbonPeriod::setFallbackLocale($locale); + CarbonInterval::setFallbackLocale($locale); + + if (class_exists(IlluminateCarbon::class) && method_exists(IlluminateCarbon::class, 'setFallbackLocale')) { + IlluminateCarbon::setFallbackLocale($locale); + } + + if (class_exists(Date::class)) { + try { + $root = Date::getFacadeRoot(); + $root->setFallbackLocale($locale); + } catch (Throwable) { // @codeCoverageIgnore // Non Carbon class in use in Date facade } } @@ -104,6 +142,19 @@ protected function getLocale() return $app ? $app->getLocale() : null; } + protected function getFallbackLocale() + { + if ($this->fallbackLocaleGetter) { + return ($this->fallbackLocaleGetter)(); + } + + $app = $this->getApp(); + + return $app && method_exists($app, 'getFallbackLocale') + ? $app->getFallbackLocale() + : $this->getGlobalApp('translator')?->getFallback(); + } + protected function getApp() { if ($this->appGetter) { diff --git a/vendor/nesbot/carbon/src/Carbon/List/languages.php b/vendor/nesbot/carbon/src/Carbon/List/languages.php index 5b5d9a1e7..557787753 100644 --- a/vendor/nesbot/carbon/src/Carbon/List/languages.php +++ b/vendor/nesbot/carbon/src/Carbon/List/languages.php @@ -1,5 +1,7 @@ 'Andorra', @@ -41,7 +68,7 @@ 'BL' => 'Saint Barthélemy', 'BM' => 'Bermuda', 'BN' => 'Brunei Darussalam', - 'BO' => 'Bolivia (Plurinational State of)', + 'BO' => 'Bolivia', 'BQ' => 'Bonaire, Sint Eustatius and Saba', 'BR' => 'Brazil', 'BS' => 'Bahamas', @@ -84,8 +111,8 @@ 'ET' => 'Ethiopia', 'FI' => 'Finland', 'FJ' => 'Fiji', - 'FK' => 'Falkland Islands (Malvinas)', - 'FM' => 'Micronesia (Federated States of)', + 'FK' => 'Falkland Islands', + 'FM' => 'Micronesia', 'FO' => 'Faroe Islands', 'FR' => 'France', 'GA' => 'Gabon', @@ -120,7 +147,7 @@ 'IN' => 'India', 'IO' => 'British Indian Ocean Territory', 'IQ' => 'Iraq', - 'IR' => 'Iran (Islamic Republic of)', + 'IR' => 'Iran', 'IS' => 'Iceland', 'IT' => 'Italy', 'JE' => 'Jersey', @@ -151,12 +178,12 @@ 'LY' => 'Libya', 'MA' => 'Morocco', 'MC' => 'Monaco', - 'MD' => 'Moldova, Republic of', + 'MD' => 'Moldova', 'ME' => 'Montenegro', 'MF' => 'Saint Martin (French part)', 'MG' => 'Madagascar', 'MH' => 'Marshall Islands', - 'MK' => 'Macedonia, the former Yugoslav Republic of', + 'MK' => 'North Macedonia', 'ML' => 'Mali', 'MM' => 'Myanmar', 'MN' => 'Mongolia', @@ -195,7 +222,7 @@ 'PM' => 'Saint Pierre and Miquelon', 'PN' => 'Pitcairn', 'PR' => 'Puerto Rico', - 'PS' => 'Palestine, State of', + 'PS' => 'Palestine', 'PT' => 'Portugal', 'PW' => 'Palau', 'PY' => 'Paraguay', @@ -240,8 +267,8 @@ 'TR' => 'Turkey', 'TT' => 'Trinidad and Tobago', 'TV' => 'Tuvalu', - 'TW' => 'Taiwan, Province of China', - 'TZ' => 'Tanzania, United Republic of', + 'TW' => 'Taiwan', + 'TZ' => 'Tanzania', 'UA' => 'Ukraine', 'UG' => 'Uganda', 'UM' => 'United States Minor Outlying Islands', @@ -250,7 +277,7 @@ 'UZ' => 'Uzbekistan', 'VA' => 'Holy See', 'VC' => 'Saint Vincent and the Grenadines', - 'VE' => 'Venezuela (Bolivarian Republic of)', + 'VE' => 'Venezuela', 'VG' => 'Virgin Islands (British)', 'VI' => 'Virgin Islands (U.S.)', 'VN' => 'Viet Nam', diff --git a/vendor/nesbot/carbon/src/Carbon/MessageFormatter/MessageFormatterMapper.php b/vendor/nesbot/carbon/src/Carbon/MessageFormatter/MessageFormatterMapper.php index c05480876..64b5d9768 100644 --- a/vendor/nesbot/carbon/src/Carbon/MessageFormatter/MessageFormatterMapper.php +++ b/vendor/nesbot/carbon/src/Carbon/MessageFormatter/MessageFormatterMapper.php @@ -1,5 +1,7 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +use Carbon\Exceptions\InvalidFormatException; + +enum Month: int +{ + // Using constants is only safe starting from PHP 8.2 + case January = 1; // CarbonInterface::JANUARY + case February = 2; // CarbonInterface::FEBRUARY + case March = 3; // CarbonInterface::MARCH + case April = 4; // CarbonInterface::APRIL + case May = 5; // CarbonInterface::MAY + case June = 6; // CarbonInterface::JUNE + case July = 7; // CarbonInterface::JULY + case August = 8; // CarbonInterface::AUGUST + case September = 9; // CarbonInterface::SEPTEMBER + case October = 10; // CarbonInterface::OCTOBER + case November = 11; // CarbonInterface::NOVEMBER + case December = 12; // CarbonInterface::DECEMBER + + public static function int(self|int|null $value): ?int + { + return $value instanceof self ? $value->value : $value; + } + + public static function fromNumber(int $number): self + { + $month = $number % CarbonInterface::MONTHS_PER_YEAR; + + return self::from($month + ($month < 1 ? CarbonInterface::MONTHS_PER_YEAR : 0)); + } + + public static function fromName(string $name, ?string $locale = null): self + { + try { + return self::from(CarbonImmutable::parseFromLocale("$name 1", $locale)->month); + } catch (InvalidFormatException $exception) { + // Possibly current language expect a dot after short name, but it's missing + if ($locale !== null && !mb_strlen($name) < 4 && !str_ends_with($name, '.')) { + try { + return self::from(CarbonImmutable::parseFromLocale("$name. 1", $locale)->month); + } catch (InvalidFormatException $e) { + // Throw previous error + } + } + + throw $exception; + } + } + + public function ofTheYear(CarbonImmutable|int|null $now = null): CarbonImmutable + { + if (\is_int($now)) { + return CarbonImmutable::create($now, $this->value); + } + + $modifier = $this->name.' 1st'; + + return $now?->modify($modifier) ?? new CarbonImmutable($modifier); + } + + public function locale(string $locale, ?CarbonImmutable $now = null): CarbonImmutable + { + return $this->ofTheYear($now)->locale($locale); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/PHPStan/AbstractMacro.php b/vendor/nesbot/carbon/src/Carbon/PHPStan/AbstractMacro.php deleted file mode 100644 index fde67b36a..000000000 --- a/vendor/nesbot/carbon/src/Carbon/PHPStan/AbstractMacro.php +++ /dev/null @@ -1,286 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Carbon\PHPStan; - -use Closure; -use InvalidArgumentException; -use PHPStan\BetterReflection\Reflection\Adapter\ReflectionParameter as AdapterReflectionParameter; -use PHPStan\BetterReflection\Reflection\Adapter\ReflectionType as AdapterReflectionType; -use PHPStan\BetterReflection\Reflection\ReflectionClass as BetterReflectionClass; -use PHPStan\BetterReflection\Reflection\ReflectionFunction as BetterReflectionFunction; -use PHPStan\BetterReflection\Reflection\ReflectionParameter as BetterReflectionParameter; -use PHPStan\Reflection\Php\BuiltinMethodReflection; -use PHPStan\TrinaryLogic; -use ReflectionClass; -use ReflectionFunction; -use ReflectionMethod; -use ReflectionParameter; -use ReflectionType; -use stdClass; -use Throwable; - -abstract class AbstractMacro implements BuiltinMethodReflection -{ - /** - * The reflection function/method. - * - * @var ReflectionFunction|ReflectionMethod - */ - protected $reflectionFunction; - - /** - * The class name. - * - * @var class-string - */ - private $className; - - /** - * The method name. - * - * @var string - */ - private $methodName; - - /** - * The parameters. - * - * @var ReflectionParameter[] - */ - private $parameters; - - /** - * The is static. - * - * @var bool - */ - private $static = false; - - /** - * Macro constructor. - * - * @param class-string $className - * @param string $methodName - * @param callable $macro - */ - public function __construct(string $className, string $methodName, $macro) - { - $this->className = $className; - $this->methodName = $methodName; - $rawReflectionFunction = \is_array($macro) - ? new ReflectionMethod($macro[0], $macro[1]) - : new ReflectionFunction($macro); - $this->reflectionFunction = self::hasModernParser() - ? $this->getReflectionFunction($macro) - : $rawReflectionFunction; // @codeCoverageIgnore - $this->parameters = array_map( - function ($parameter) { - if ($parameter instanceof BetterReflectionParameter) { - return new AdapterReflectionParameter($parameter); - } - - return $parameter; // @codeCoverageIgnore - }, - $this->reflectionFunction->getParameters() - ); - - if ($rawReflectionFunction->isClosure()) { - try { - $closure = $rawReflectionFunction->getClosure(); - $boundClosure = Closure::bind($closure, new stdClass()); - $this->static = (!$boundClosure || (new ReflectionFunction($boundClosure))->getClosureThis() === null); - } catch (Throwable $e) { - $this->static = true; - } - } - } - - private function getReflectionFunction($spec) - { - if (\is_array($spec) && \count($spec) === 2 && \is_string($spec[1])) { - \assert($spec[1] !== ''); - - if (\is_object($spec[0])) { - return BetterReflectionClass::createFromInstance($spec[0]) - ->getMethod($spec[1]); - } - - return BetterReflectionClass::createFromName($spec[0]) - ->getMethod($spec[1]); - } - - if (\is_string($spec)) { - return BetterReflectionFunction::createFromName($spec); - } - - if ($spec instanceof Closure) { - return BetterReflectionFunction::createFromClosure($spec); - } - - throw new InvalidArgumentException('Could not create reflection from the spec given'); // @codeCoverageIgnore - } - - /** - * {@inheritdoc} - */ - public function getDeclaringClass(): ReflectionClass - { - return new ReflectionClass($this->className); - } - - /** - * {@inheritdoc} - */ - public function isPrivate(): bool - { - return false; - } - - /** - * {@inheritdoc} - */ - public function isPublic(): bool - { - return true; - } - - /** - * {@inheritdoc} - */ - public function isFinal(): bool - { - return false; - } - - /** - * {@inheritdoc} - */ - public function isInternal(): bool - { - return false; - } - - /** - * {@inheritdoc} - */ - public function isAbstract(): bool - { - return false; - } - - /** - * {@inheritdoc} - */ - public function isStatic(): bool - { - return $this->static; - } - - /** - * {@inheritdoc} - */ - public function getDocComment(): ?string - { - return $this->reflectionFunction->getDocComment() ?: null; - } - - /** - * {@inheritdoc} - */ - public function getName(): string - { - return $this->methodName; - } - - /** - * {@inheritdoc} - */ - public function getParameters(): array - { - return $this->parameters; - } - - /** - * {@inheritdoc} - */ - public function getReturnType(): ?ReflectionType - { - $type = $this->reflectionFunction->getReturnType(); - - if ($type instanceof ReflectionType) { - return $type; // @codeCoverageIgnore - } - - return self::adaptType($type); - } - - /** - * {@inheritdoc} - */ - public function isDeprecated(): TrinaryLogic - { - return TrinaryLogic::createFromBoolean( - $this->reflectionFunction->isDeprecated() || - preg_match('/@deprecated/i', $this->getDocComment() ?: '') - ); - } - - /** - * {@inheritdoc} - */ - public function isVariadic(): bool - { - return $this->reflectionFunction->isVariadic(); - } - - /** - * {@inheritdoc} - */ - public function getPrototype(): BuiltinMethodReflection - { - return $this; - } - - public function getTentativeReturnType(): ?ReflectionType - { - return null; - } - - public function returnsByReference(): TrinaryLogic - { - return TrinaryLogic::createNo(); - } - - private static function adaptType($type) - { - $method = method_exists(AdapterReflectionType::class, 'fromTypeOrNull') - ? 'fromTypeOrNull' - : 'fromReturnTypeOrNull'; // @codeCoverageIgnore - - return AdapterReflectionType::$method($type); - } - - private static function hasModernParser(): bool - { - static $modernParser = null; - - if ($modernParser !== null) { - return $modernParser; - } - - $modernParser = method_exists(AdapterReflectionType::class, 'fromTypeOrNull'); - - return $modernParser; - } -} diff --git a/vendor/nesbot/carbon/src/Carbon/PHPStan/Macro.php b/vendor/nesbot/carbon/src/Carbon/PHPStan/Macro.php deleted file mode 100644 index de3e51f6a..000000000 --- a/vendor/nesbot/carbon/src/Carbon/PHPStan/Macro.php +++ /dev/null @@ -1,34 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Carbon\PHPStan; - -use PHPStan\BetterReflection\Reflection\Adapter; -use PHPStan\Reflection\Php\BuiltinMethodReflection; -use ReflectionMethod; - -$method = new ReflectionMethod(BuiltinMethodReflection::class, 'getReflection'); - -require $method->hasReturnType() && $method->getReturnType()->getName() === Adapter\ReflectionMethod::class - ? __DIR__.'/../../../lazy/Carbon/PHPStan/AbstractMacroStatic.php' - : __DIR__.'/../../../lazy/Carbon/PHPStan/AbstractMacroBuiltin.php'; - -$method = new ReflectionMethod(BuiltinMethodReflection::class, 'getFileName'); - -require $method->hasReturnType() - ? __DIR__.'/../../../lazy/Carbon/PHPStan/MacroStrongType.php' - : __DIR__.'/../../../lazy/Carbon/PHPStan/MacroWeakType.php'; - -final class Macro extends LazyMacro -{ -} diff --git a/vendor/nesbot/carbon/src/Carbon/PHPStan/MacroExtension.php b/vendor/nesbot/carbon/src/Carbon/PHPStan/MacroExtension.php index 2cd6fce54..7327586a6 100644 --- a/vendor/nesbot/carbon/src/Carbon/PHPStan/MacroExtension.php +++ b/vendor/nesbot/carbon/src/Carbon/PHPStan/MacroExtension.php @@ -1,5 +1,7 @@ scanner = new MacroScanner($reflectionProvider); - $this->methodReflectionFactory = $methodReflectionFactory; + $this->reflectionProvider = $reflectionProvider; + $this->closureTypeFactory = $closureTypeFactory; } /** @@ -55,7 +63,17 @@ public function __construct( */ public function hasMethod(ClassReflection $classReflection, string $methodName): bool { - return $this->scanner->hasMethod($classReflection->getName(), $methodName); + if ( + $classReflection->getName() !== CarbonInterface::class && + !$classReflection->isSubclassOf(CarbonInterface::class) + ) { + return false; + } + + $className = $classReflection->getName(); + + return \is_callable([$className, 'hasMacro']) && + $className::hasMacro($methodName); } /** @@ -63,26 +81,57 @@ public function hasMethod(ClassReflection $classReflection, string $methodName): */ public function getMethod(ClassReflection $classReflection, string $methodName): MethodReflection { - $builtinMacro = $this->scanner->getMethod($classReflection->getName(), $methodName); - $supportAssertions = class_exists(Assertions::class); + $macros = FactoryImmutable::getDefaultInstance()->getSettings()['macros'] ?? []; + $macro = $macros[$methodName] ?? throw new InvalidArgumentException("Macro '$methodName' not found"); + $static = false; + $final = false; + $deprecated = false; + $docComment = null; + + if (\is_array($macro) && \count($macro) === 2 && \is_string($macro[1])) { + \assert($macro[1] !== ''); + + $reflection = new ReflectionMethod($macro[0], $macro[1]); + $closure = \is_object($macro[0]) ? $reflection->getClosure($macro[0]) : $reflection->getClosure(); + + $static = $reflection->isStatic(); + $final = $reflection->isFinal(); + $deprecated = $reflection->isDeprecated(); + $docComment = $reflection->getDocComment() ?: null; + } elseif (\is_string($macro)) { + $reflection = new ReflectionFunction($macro); + $closure = $reflection->getClosure(); + $deprecated = $reflection->isDeprecated(); + $docComment = $reflection->getDocComment() ?: null; + } elseif ($macro instanceof Closure) { + $closure = $macro; + + try { + $boundClosure = Closure::bind($closure, new stdClass()); + $static = (!$boundClosure || (new ReflectionFunction($boundClosure))->getClosureThis() === null); + } catch (Throwable) { + $static = true; + } + + $reflection = new ReflectionFunction($macro); + $deprecated = $reflection->isDeprecated(); + $docComment = $reflection->getDocComment() ?: null; + } + + if (!isset($closure)) { + throw new InvalidArgumentException('Could not create reflection from the spec given'); // @codeCoverageIgnore + } + + $closureType = $this->closureTypeFactory->fromClosureObject($closure); - return $this->methodReflectionFactory->create( + return new MacroMethodReflection( $classReflection, - null, - $builtinMacro, - $classReflection->getActiveTemplateTypeMap(), - [], - TypehintHelper::decideTypeFromReflection($builtinMacro->getReturnType()), - null, - null, - $builtinMacro->isDeprecated()->yes(), - $builtinMacro->isInternal(), - $builtinMacro->isFinal(), - $supportAssertions ? null : $builtinMacro->getDocComment(), - $supportAssertions ? Assertions::createEmpty() : null, - null, - $builtinMacro->getDocComment(), - [] + $methodName, + $closureType, + $static, + $final, + $deprecated, + $docComment, ); } } diff --git a/vendor/nesbot/carbon/src/Carbon/PHPStan/MacroMethodReflection.php b/vendor/nesbot/carbon/src/Carbon/PHPStan/MacroMethodReflection.php new file mode 100644 index 000000000..b710f5492 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/PHPStan/MacroMethodReflection.php @@ -0,0 +1,124 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\PHPStan; + +use PHPStan\Reflection\ClassReflection; +use PHPStan\Reflection\MethodReflection; +use PHPStan\Reflection\ParametersAcceptor; +use PHPStan\TrinaryLogic; +use PHPStan\Type\Type; + +use function preg_match; + +class MacroMethodReflection implements MethodReflection +{ + private ClassReflection $declaringClass; + private string $methodName; + private ParametersAcceptor $macroClosureType; + private bool $static; + private bool $final; + private bool $deprecated; + private ?string $docComment; + + public function __construct( + ClassReflection $declaringClass, + string $methodName, + ParametersAcceptor $macroClosureType, + bool $static, + bool $final, + bool $deprecated, + ?string $docComment + ) { + $this->declaringClass = $declaringClass; + $this->methodName = $methodName; + $this->macroClosureType = $macroClosureType; + $this->static = $static; + $this->final = $final; + $this->deprecated = $deprecated; + $this->docComment = $docComment; + } + + public function getDeclaringClass(): ClassReflection + { + return $this->declaringClass; + } + + public function isStatic(): bool + { + return $this->static; + } + + public function isPrivate(): bool + { + return false; + } + + public function isPublic(): bool + { + return true; + } + + public function getDocComment(): ?string + { + return $this->docComment; + } + + public function getName(): string + { + return $this->methodName; + } + + public function getPrototype(): \PHPStan\Reflection\ClassMemberReflection + { + return $this; + } + + public function getVariants(): array + { + return [$this->macroClosureType]; + } + + public function isDeprecated(): TrinaryLogic + { + return TrinaryLogic::createFromBoolean( + $this->deprecated || + preg_match('/@deprecated/i', $this->getDocComment() ?: '') + ); + } + + public function getDeprecatedDescription(): ?string + { + return null; + } + + public function isFinal(): TrinaryLogic + { + return TrinaryLogic::createFromBoolean($this->final); + } + + public function isInternal(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + public function getThrowType(): ?Type + { + return null; + } + + public function hasSideEffects(): TrinaryLogic + { + return TrinaryLogic::createMaybe(); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/PHPStan/MacroScanner.php b/vendor/nesbot/carbon/src/Carbon/PHPStan/MacroScanner.php deleted file mode 100644 index eb8957d4d..000000000 --- a/vendor/nesbot/carbon/src/Carbon/PHPStan/MacroScanner.php +++ /dev/null @@ -1,83 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Carbon\PHPStan; - -use Carbon\CarbonInterface; -use PHPStan\Reflection\ReflectionProvider; -use ReflectionClass; -use ReflectionException; - -final class MacroScanner -{ - /** - * @var \PHPStan\Reflection\ReflectionProvider - */ - private $reflectionProvider; - - /** - * MacroScanner constructor. - * - * @param \PHPStan\Reflection\ReflectionProvider $reflectionProvider - */ - public function __construct(ReflectionProvider $reflectionProvider) - { - $this->reflectionProvider = $reflectionProvider; - } - - /** - * Return true if the given pair class-method is a Carbon macro. - * - * @param class-string $className - * @param string $methodName - * - * @return bool - */ - public function hasMethod(string $className, string $methodName): bool - { - $classReflection = $this->reflectionProvider->getClass($className); - - if ( - $classReflection->getName() !== CarbonInterface::class && - !$classReflection->isSubclassOf(CarbonInterface::class) - ) { - return false; - } - - return \is_callable([$className, 'hasMacro']) && - $className::hasMacro($methodName); - } - - /** - * Return the Macro for a given pair class-method. - * - * @param class-string $className - * @param string $methodName - * - * @throws ReflectionException - * - * @return Macro - */ - public function getMethod(string $className, string $methodName): Macro - { - $reflectionClass = new ReflectionClass($className); - $property = $reflectionClass->getProperty('globalMacros'); - - $property->setAccessible(true); - $macro = $property->getValue()[$methodName]; - - return new Macro( - $className, - $methodName, - $macro - ); - } -} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Boundaries.php b/vendor/nesbot/carbon/src/Carbon/Traits/Boundaries.php index 71bbb7230..b21b9c54e 100644 --- a/vendor/nesbot/carbon/src/Carbon/Traits/Boundaries.php +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Boundaries.php @@ -1,5 +1,7 @@ startOfWeek(Carbon::SUNDAY) . "\n"; * ``` * - * @param int $weekStartsAt optional start allow you to specify the day of week to use to start the week + * @param WeekDay|int|null $weekStartsAt optional start allow you to specify the day of week to use to start the week * * @return static */ - public function startOfWeek($weekStartsAt = null) + public function startOfWeek(WeekDay|int|null $weekStartsAt = null): static { - return $this->subDays((7 + $this->dayOfWeek - ($weekStartsAt ?? $this->firstWeekDay)) % 7)->startOfDay(); + return $this + ->subDays( + (static::DAYS_PER_WEEK + $this->dayOfWeek - (WeekDay::int($weekStartsAt) ?? $this->firstWeekDay)) % + static::DAYS_PER_WEEK, + ) + ->startOfDay(); } /** @@ -286,13 +295,18 @@ public function startOfWeek($weekStartsAt = null) * echo Carbon::parse('2018-07-25 12:45:16')->endOfWeek(Carbon::SATURDAY) . "\n"; * ``` * - * @param int $weekEndsAt optional start allow you to specify the day of week to use to end the week + * @param WeekDay|int|null $weekEndsAt optional end allow you to specify the day of week to use to end the week * * @return static */ - public function endOfWeek($weekEndsAt = null) + public function endOfWeek(WeekDay|int|null $weekEndsAt = null): static { - return $this->addDays((7 - $this->dayOfWeek + ($weekEndsAt ?? $this->lastWeekDay)) % 7)->endOfDay(); + return $this + ->addDays( + (static::DAYS_PER_WEEK - $this->dayOfWeek + (WeekDay::int($weekEndsAt) ?? $this->lastWeekDay)) % + static::DAYS_PER_WEEK, + ) + ->endOfDay(); } /** @@ -302,10 +316,8 @@ public function endOfWeek($weekEndsAt = null) * ``` * echo Carbon::parse('2018-07-25 12:45:16')->startOfHour(); * ``` - * - * @return static */ - public function startOfHour() + public function startOfHour(): static { return $this->setTime($this->hour, 0, 0, 0); } @@ -317,10 +329,8 @@ public function startOfHour() * ``` * echo Carbon::parse('2018-07-25 12:45:16')->endOfHour(); * ``` - * - * @return static */ - public function endOfHour() + public function endOfHour(): static { return $this->setTime($this->hour, static::MINUTES_PER_HOUR - 1, static::SECONDS_PER_MINUTE - 1, static::MICROSECONDS_PER_SECOND - 1); } @@ -332,10 +342,8 @@ public function endOfHour() * ``` * echo Carbon::parse('2018-07-25 12:45:16')->startOfMinute(); * ``` - * - * @return static */ - public function startOfMinute() + public function startOfMinute(): static { return $this->setTime($this->hour, $this->minute, 0, 0); } @@ -347,10 +355,8 @@ public function startOfMinute() * ``` * echo Carbon::parse('2018-07-25 12:45:16')->endOfMinute(); * ``` - * - * @return static */ - public function endOfMinute() + public function endOfMinute(): static { return $this->setTime($this->hour, $this->minute, static::SECONDS_PER_MINUTE - 1, static::MICROSECONDS_PER_SECOND - 1); } @@ -364,10 +370,8 @@ public function endOfMinute() * ->startOfSecond() * ->format('H:i:s.u'); * ``` - * - * @return static */ - public function startOfSecond() + public function startOfSecond(): static { return $this->setTime($this->hour, $this->minute, $this->second, 0); } @@ -381,32 +385,59 @@ public function startOfSecond() * ->endOfSecond() * ->format('H:i:s.u'); * ``` - * - * @return static */ - public function endOfSecond() + public function endOfSecond(): static { return $this->setTime($this->hour, $this->minute, $this->second, static::MICROSECONDS_PER_SECOND - 1); } /** - * Modify to start of current given unit. + * Modify to start of current millisecond, microseconds such as 12345 become 123000 * * @example * ``` * echo Carbon::parse('2018-07-25 12:45:16.334455') - * ->startOf('month') - * ->endOf('week', Carbon::FRIDAY); + * ->startOfSecond() + * ->format('H:i:s.u'); * ``` + */ + public function startOfMillisecond(): static + { + $millisecond = (int) floor($this->micro / 1000); + + return $this->setTime($this->hour, $this->minute, $this->second, $millisecond * 1000); + } + + /** + * Modify to end of current millisecond, microseconds such as 12345 become 123999 * - * @param string $unit - * @param array $params + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16.334455') + * ->endOfSecond() + * ->format('H:i:s.u'); + * ``` + */ + public function endOfMillisecond(): static + { + $millisecond = (int) floor($this->micro / 1000); + + return $this->setTime($this->hour, $this->minute, $this->second, $millisecond * 1000 + 999); + } + + /** + * Modify to start of current given unit. * - * @return static + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16.334455') + * ->startOf(Unit::Month) + * ->endOf(Unit::Week, Carbon::FRIDAY); + * ``` */ - public function startOf($unit, ...$params) + public function startOf(Unit|string $unit, mixed ...$params): static { - $ucfUnit = ucfirst(static::singularUnit($unit)); + $ucfUnit = ucfirst($unit instanceof Unit ? $unit->value : static::singularUnit($unit)); $method = "startOf$ucfUnit"; if (!method_exists($this, $method)) { throw new UnknownUnitException($unit); @@ -421,18 +452,13 @@ public function startOf($unit, ...$params) * @example * ``` * echo Carbon::parse('2018-07-25 12:45:16.334455') - * ->startOf('month') - * ->endOf('week', Carbon::FRIDAY); + * ->startOf(Unit::Month) + * ->endOf(Unit::Week, Carbon::FRIDAY); * ``` - * - * @param string $unit - * @param array $params - * - * @return static */ - public function endOf($unit, ...$params) + public function endOf(Unit|string $unit, mixed ...$params): static { - $ucfUnit = ucfirst(static::singularUnit($unit)); + $ucfUnit = ucfirst($unit instanceof Unit ? $unit->value : static::singularUnit($unit)); $method = "endOf$ucfUnit"; if (!method_exists($this, $method)) { throw new UnknownUnitException($unit); diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Cast.php b/vendor/nesbot/carbon/src/Carbon/Traits/Cast.php index 5f7c7c011..4c00e42cc 100644 --- a/vendor/nesbot/carbon/src/Carbon/Traits/Cast.php +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Cast.php @@ -1,5 +1,7 @@ $className The $className::instance() method will be called to cast the current object. * - * @return DateTimeInterface + * @return T */ - public function cast(string $className) + public function cast(string $className): mixed { if (!method_exists($className, 'instance')) { if (is_a($className, DateTimeInterface::class, true)) { - return new $className($this->rawFormat('Y-m-d H:i:s.u'), $this->getTimezone()); + return $className::createFromFormat('U.u', $this->rawFormat('U.u')) + ->setTimezone($this->getTimezone()); } throw new InvalidCastException("$className has not the instance() method needed to cast the date."); diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Comparison.php b/vendor/nesbot/carbon/src/Carbon/Traits/Comparison.php index daee19cc9..53e54deae 100644 --- a/vendor/nesbot/carbon/src/Carbon/Traits/Comparison.php +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Comparison.php @@ -1,5 +1,7 @@ eq('2018-07-25 12:45:17'); // false * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * * @see equalTo() - * - * @return bool */ - public function eq($date): bool + public function eq(DateTimeInterface|string $date): bool { return $this->equalTo($date); } @@ -68,16 +73,9 @@ public function eq($date): bool * Carbon::parse('2018-07-25 12:45:16')->equalTo(Carbon::parse('2018-07-25 12:45:16')); // true * Carbon::parse('2018-07-25 12:45:16')->equalTo('2018-07-25 12:45:17'); // false * ``` - * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * - * @return bool */ - public function equalTo($date): bool + public function equalTo(DateTimeInterface|string $date): bool { - $this->discourageNull($date); - $this->discourageBoolean($date); - return $this == $this->resolveCarbon($date); } @@ -91,13 +89,9 @@ public function equalTo($date): bool * Carbon::parse('2018-07-25 12:45:16')->ne('2018-07-25 12:45:17'); // true * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * * @see notEqualTo() - * - * @return bool */ - public function ne($date): bool + public function ne(DateTimeInterface|string $date): bool { return $this->notEqualTo($date); } @@ -111,12 +105,8 @@ public function ne($date): bool * Carbon::parse('2018-07-25 12:45:16')->notEqualTo(Carbon::parse('2018-07-25 12:45:16')); // false * Carbon::parse('2018-07-25 12:45:16')->notEqualTo('2018-07-25 12:45:17'); // true * ``` - * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * - * @return bool */ - public function notEqualTo($date): bool + public function notEqualTo(DateTimeInterface|string $date): bool { return !$this->equalTo($date); } @@ -131,13 +121,9 @@ public function notEqualTo($date): bool * Carbon::parse('2018-07-25 12:45:16')->gt('2018-07-25 12:45:17'); // false * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * * @see greaterThan() - * - * @return bool */ - public function gt($date): bool + public function gt(DateTimeInterface|string $date): bool { return $this->greaterThan($date); } @@ -151,16 +137,9 @@ public function gt($date): bool * Carbon::parse('2018-07-25 12:45:16')->greaterThan('2018-07-25 12:45:16'); // false * Carbon::parse('2018-07-25 12:45:16')->greaterThan('2018-07-25 12:45:17'); // false * ``` - * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * - * @return bool */ - public function greaterThan($date): bool + public function greaterThan(DateTimeInterface|string $date): bool { - $this->discourageNull($date); - $this->discourageBoolean($date); - return $this > $this->resolveCarbon($date); } @@ -174,13 +153,9 @@ public function greaterThan($date): bool * Carbon::parse('2018-07-25 12:45:16')->isAfter('2018-07-25 12:45:17'); // false * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * * @see greaterThan() - * - * @return bool */ - public function isAfter($date): bool + public function isAfter(DateTimeInterface|string $date): bool { return $this->greaterThan($date); } @@ -195,13 +170,9 @@ public function isAfter($date): bool * Carbon::parse('2018-07-25 12:45:16')->gte('2018-07-25 12:45:17'); // false * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * * @see greaterThanOrEqualTo() - * - * @return bool */ - public function gte($date): bool + public function gte(DateTimeInterface|string $date): bool { return $this->greaterThanOrEqualTo($date); } @@ -215,16 +186,9 @@ public function gte($date): bool * Carbon::parse('2018-07-25 12:45:16')->greaterThanOrEqualTo('2018-07-25 12:45:16'); // true * Carbon::parse('2018-07-25 12:45:16')->greaterThanOrEqualTo('2018-07-25 12:45:17'); // false * ``` - * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * - * @return bool */ - public function greaterThanOrEqualTo($date): bool + public function greaterThanOrEqualTo(DateTimeInterface|string $date): bool { - $this->discourageNull($date); - $this->discourageBoolean($date); - return $this >= $this->resolveCarbon($date); } @@ -238,13 +202,9 @@ public function greaterThanOrEqualTo($date): bool * Carbon::parse('2018-07-25 12:45:16')->lt('2018-07-25 12:45:17'); // true * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * * @see lessThan() - * - * @return bool */ - public function lt($date): bool + public function lt(DateTimeInterface|string $date): bool { return $this->lessThan($date); } @@ -258,16 +218,9 @@ public function lt($date): bool * Carbon::parse('2018-07-25 12:45:16')->lessThan('2018-07-25 12:45:16'); // false * Carbon::parse('2018-07-25 12:45:16')->lessThan('2018-07-25 12:45:17'); // true * ``` - * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * - * @return bool */ - public function lessThan($date): bool + public function lessThan(DateTimeInterface|string $date): bool { - $this->discourageNull($date); - $this->discourageBoolean($date); - return $this < $this->resolveCarbon($date); } @@ -281,13 +234,9 @@ public function lessThan($date): bool * Carbon::parse('2018-07-25 12:45:16')->isBefore('2018-07-25 12:45:17'); // true * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * * @see lessThan() - * - * @return bool */ - public function isBefore($date): bool + public function isBefore(DateTimeInterface|string $date): bool { return $this->lessThan($date); } @@ -302,13 +251,9 @@ public function isBefore($date): bool * Carbon::parse('2018-07-25 12:45:16')->lte('2018-07-25 12:45:17'); // true * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * * @see lessThanOrEqualTo() - * - * @return bool */ - public function lte($date): bool + public function lte(DateTimeInterface|string $date): bool { return $this->lessThanOrEqualTo($date); } @@ -322,16 +267,9 @@ public function lte($date): bool * Carbon::parse('2018-07-25 12:45:16')->lessThanOrEqualTo('2018-07-25 12:45:16'); // true * Carbon::parse('2018-07-25 12:45:16')->lessThanOrEqualTo('2018-07-25 12:45:17'); // true * ``` - * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date - * - * @return bool */ - public function lessThanOrEqualTo($date): bool + public function lessThanOrEqualTo(DateTimeInterface|string $date): bool { - $this->discourageNull($date); - $this->discourageBoolean($date); - return $this <= $this->resolveCarbon($date); } @@ -350,13 +288,9 @@ public function lessThanOrEqualTo($date): bool * Carbon::parse('2018-07-25')->between('2018-07-25', '2018-08-01', false); // false * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1 - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2 - * @param bool $equal Indicates if an equal to comparison should be done - * - * @return bool + * @param bool $equal Indicates if an equal to comparison should be done */ - public function between($date1, $date2, $equal = true): bool + public function between(DateTimeInterface|string $date1, DateTimeInterface|string $date2, bool $equal = true): bool { $date1 = $this->resolveCarbon($date1); $date2 = $this->resolveCarbon($date2); @@ -381,13 +315,8 @@ public function between($date1, $date2, $equal = true): bool * Carbon::parse('2018-07-25')->betweenIncluded('2018-08-01', '2018-08-20'); // false * Carbon::parse('2018-07-25')->betweenIncluded('2018-07-25', '2018-08-01'); // true * ``` - * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1 - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2 - * - * @return bool */ - public function betweenIncluded($date1, $date2): bool + public function betweenIncluded(DateTimeInterface|string $date1, DateTimeInterface|string $date2): bool { return $this->between($date1, $date2, true); } @@ -401,13 +330,8 @@ public function betweenIncluded($date1, $date2): bool * Carbon::parse('2018-07-25')->betweenExcluded('2018-08-01', '2018-08-20'); // false * Carbon::parse('2018-07-25')->betweenExcluded('2018-07-25', '2018-08-01'); // false * ``` - * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1 - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2 - * - * @return bool */ - public function betweenExcluded($date1, $date2): bool + public function betweenExcluded(DateTimeInterface|string $date1, DateTimeInterface|string $date2): bool { return $this->between($date1, $date2, false); } @@ -423,13 +347,9 @@ public function betweenExcluded($date1, $date2): bool * Carbon::parse('2018-07-25')->isBetween('2018-07-25', '2018-08-01', false); // false * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1 - * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2 - * @param bool $equal Indicates if an equal to comparison should be done - * - * @return bool + * @param bool $equal Indicates if an equal to comparison should be done */ - public function isBetween($date1, $date2, $equal = true): bool + public function isBetween(DateTimeInterface|string $date1, DateTimeInterface|string $date2, bool $equal = true): bool { return $this->between($date1, $date2, $equal); } @@ -442,10 +362,8 @@ public function isBetween($date1, $date2, $equal = true): bool * Carbon::parse('2019-07-14')->isWeekday(); // false * Carbon::parse('2019-07-15')->isWeekday(); // true * ``` - * - * @return bool */ - public function isWeekday() + public function isWeekday(): bool { return !$this->isWeekend(); } @@ -458,12 +376,14 @@ public function isWeekday() * Carbon::parse('2019-07-14')->isWeekend(); // true * Carbon::parse('2019-07-15')->isWeekend(); // false * ``` - * - * @return bool */ - public function isWeekend() + public function isWeekend(): bool { - return \in_array($this->dayOfWeek, static::$weekendDays, true); + return \in_array( + $this->dayOfWeek, + $this->transmitFactory(static fn () => static::getWeekendDays()), + true, + ); } /** @@ -474,12 +394,12 @@ public function isWeekend() * Carbon::yesterday()->isYesterday(); // true * Carbon::tomorrow()->isYesterday(); // false * ``` - * - * @return bool */ - public function isYesterday() + public function isYesterday(): bool { - return $this->toDateString() === static::yesterday($this->getTimezone())->toDateString(); + return $this->toDateString() === $this->transmitFactory( + fn () => static::yesterday($this->getTimezone())->toDateString(), + ); } /** @@ -490,10 +410,8 @@ public function isYesterday() * Carbon::today()->isToday(); // true * Carbon::tomorrow()->isToday(); // false * ``` - * - * @return bool */ - public function isToday() + public function isToday(): bool { return $this->toDateString() === $this->nowWithSameTz()->toDateString(); } @@ -506,12 +424,12 @@ public function isToday() * Carbon::tomorrow()->isTomorrow(); // true * Carbon::yesterday()->isTomorrow(); // false * ``` - * - * @return bool */ - public function isTomorrow() + public function isTomorrow(): bool { - return $this->toDateString() === static::tomorrow($this->getTimezone())->toDateString(); + return $this->toDateString() === $this->transmitFactory( + fn () => static::tomorrow($this->getTimezone())->toDateString(), + ); } /** @@ -522,10 +440,8 @@ public function isTomorrow() * Carbon::now()->addHours(5)->isFuture(); // true * Carbon::now()->subHours(5)->isFuture(); // false * ``` - * - * @return bool */ - public function isFuture() + public function isFuture(): bool { return $this->greaterThan($this->nowWithSameTz()); } @@ -538,14 +454,42 @@ public function isFuture() * Carbon::now()->subHours(5)->isPast(); // true * Carbon::now()->addHours(5)->isPast(); // false * ``` - * - * @return bool */ - public function isPast() + public function isPast(): bool { return $this->lessThan($this->nowWithSameTz()); } + /** + * Determines if the instance is now or in the future, ie. greater (after) than or equal to now. + * + * @example + * ``` + * Carbon::now()->isNowOrFuture(); // true + * Carbon::now()->addHours(5)->isNowOrFuture(); // true + * Carbon::now()->subHours(5)->isNowOrFuture(); // false + * ``` + */ + public function isNowOrFuture(): bool + { + return $this->greaterThanOrEqualTo($this->nowWithSameTz()); + } + + /** + * Determines if the instance is now or in the past, ie. less (before) than or equal to now. + * + * @example + * ``` + * Carbon::now()->isNowOrPast(); // true + * Carbon::now()->subHours(5)->isNowOrPast(); // true + * Carbon::now()->addHours(5)->isNowOrPast(); // false + * ``` + */ + public function isNowOrPast(): bool + { + return $this->lessThanOrEqualTo($this->nowWithSameTz()); + } + /** * Determines if the instance is a leap year. * @@ -554,10 +498,8 @@ public function isPast() * Carbon::parse('2020-01-01')->isLeapYear(); // true * Carbon::parse('2019-01-01')->isLeapYear(); // false * ``` - * - * @return bool */ - public function isLeapYear() + public function isLeapYear(): bool { return $this->rawFormat('L') === '1'; } @@ -577,12 +519,10 @@ public function isLeapYear() * ``` * * @see https://en.wikipedia.org/wiki/ISO_8601#Week_dates - * - * @return bool */ - public function isLongYear() + public function isLongYear(): bool { - return static::create($this->year, 12, 28, 0, 0, 0, $this->tz)->weekOfYear === 53; + return static::create($this->year, 12, 28, 0, 0, 0, $this->tz)->weekOfYear === static::WEEKS_PER_YEAR + 1; } /** @@ -598,10 +538,8 @@ public function isLongYear() * ``` * * @see https://en.wikipedia.org/wiki/ISO_8601#Week_dates - * - * @return bool */ - public function isLongIsoYear() + public function isLongIsoYear(): bool { return static::create($this->isoWeekYear, 12, 28, 0, 0, 0, $this->tz)->weekOfYear === 53; } @@ -615,12 +553,10 @@ public function isLongIsoYear() * Carbon::parse('2019-06-13')->isSameAs('Y-d', Carbon::parse('2019-06-14')); // false * ``` * - * @param string $format date formats to compare. - * @param \Carbon\Carbon|\DateTimeInterface|string|null $date instance to compare with or null to use current day. - * - * @return bool + * @param string $format date formats to compare. + * @param DateTimeInterface|string $date instance to compare with or null to use current day. */ - public function isSameAs($format, $date = null) + public function isSameAs(string $format, DateTimeInterface|string $date): bool { return $this->rawFormat($format) === $this->resolveCarbon($date)->rawFormat($format); } @@ -634,19 +570,27 @@ public function isSameAs($format, $date = null) * Carbon::parse('2018-12-13')->isSameUnit('year', Carbon::parse('2019-12-25')); // false * ``` * - * @param string $unit singular unit string - * @param \Carbon\Carbon|\DateTimeInterface|null $date instance to compare with or null to use current day. + * @param string $unit singular unit string + * @param DateTimeInterface|string $date instance to compare with or null to use current day. * * @throws BadComparisonUnitException * * @return bool */ - public function isSameUnit($unit, $date = null) + public function isSameUnit(string $unit, DateTimeInterface|string $date): bool { + if ($unit === /* @call isSameUnit */ 'quarter') { + $other = $this->resolveCarbon($date); + + return $other->year === $this->year && $other->quarter === $this->quarter; + } + $units = [ // @call isSameUnit 'year' => 'Y', // @call isSameUnit + 'month' => 'Y-n', + // @call isSameUnit 'week' => 'o-W', // @call isSameUnit 'day' => 'Y-m-d', @@ -657,6 +601,10 @@ public function isSameUnit($unit, $date = null) // @call isSameUnit 'second' => 'Y-m-d H:i:s', // @call isSameUnit + 'milli' => 'Y-m-d H:i:s.v', + // @call isSameUnit + 'millisecond' => 'Y-m-d H:i:s.v', + // @call isSameUnit 'micro' => 'Y-m-d H:i:s.u', // @call isSameUnit 'microsecond' => 'Y-m-d H:i:s.u', @@ -670,7 +618,7 @@ public function isSameUnit($unit, $date = null) return $this->resolveCarbon($date)->$unit === $this->$unit; } - if ($this->localStrictModeEnabled ?? static::isStrictModeEnabled()) { + if ($this->isLocalStrictModeEnabled()) { throw new BadComparisonUnitException($unit); } @@ -689,12 +637,10 @@ public function isSameUnit($unit, $date = null) * @param string $unit The unit to test. * * @throws BadMethodCallException - * - * @return bool */ - public function isCurrentUnit($unit) + public function isCurrentUnit(string $unit): bool { - return $this->{'isSame'.ucfirst($unit)}(); + return $this->{'isSame'.ucfirst($unit)}('now'); } /** @@ -708,12 +654,12 @@ public function isCurrentUnit($unit) * Carbon::parse('2019-01-12')->isSameQuarter(Carbon::parse('2018-03-01'), false); // true * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|string|null $date The instance to compare with or null to use current day. - * @param bool $ofSameYear Check if it is the same month in the same year. + * @param DateTimeInterface|string $date The instance to compare with or null to use current day. + * @param bool $ofSameYear Check if it is the same month in the same year. * * @return bool */ - public function isSameQuarter($date = null, $ofSameYear = true) + public function isSameQuarter(DateTimeInterface|string $date, bool $ofSameYear = true): bool { $date = $this->resolveCarbon($date); @@ -731,12 +677,12 @@ public function isSameQuarter($date = null, $ofSameYear = true) * Carbon::parse('2019-01-12')->isSameMonth(Carbon::parse('2018-01-01'), false); // true * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use the current date. - * @param bool $ofSameYear Check if it is the same month in the same year. + * @param DateTimeInterface|string $date The instance to compare with or null to use the current date. + * @param bool $ofSameYear Check if it is the same month in the same year. * * @return bool */ - public function isSameMonth($date = null, $ofSameYear = true) + public function isSameMonth(DateTimeInterface|string $date, bool $ofSameYear = true): bool { return $this->isSameAs($ofSameYear ? 'Y-m' : 'm', $date); } @@ -752,11 +698,11 @@ public function isSameMonth($date = null, $ofSameYear = true) * Carbon::parse('2019-07-17')->isDayOfWeek('Friday'); // false * ``` * - * @param int $dayOfWeek + * @param int|string $dayOfWeek * * @return bool */ - public function isDayOfWeek($dayOfWeek) + public function isDayOfWeek($dayOfWeek): bool { if (\is_string($dayOfWeek) && \defined($constant = static::class.'::'.strtoupper($dayOfWeek))) { $dayOfWeek = \constant($constant); @@ -776,13 +722,13 @@ public function isDayOfWeek($dayOfWeek) * Carbon::parse('2019-06-05')->isBirthday(Carbon::parse('2001-06-06')); // false * ``` * - * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use current day. + * @param DateTimeInterface|string|null $date The instance to compare with or null to use current day. * * @return bool */ - public function isBirthday($date = null) + public function isBirthday(DateTimeInterface|string|null $date = null): bool { - return $this->isSameAs('md', $date); + return $this->isSameAs('md', $date ?? 'now'); } /** @@ -796,14 +742,148 @@ public function isBirthday($date = null) * Carbon::parse('2019-03-31')->isLastOfMonth(); // true * Carbon::parse('2019-04-30')->isLastOfMonth(); // true * ``` - * - * @return bool */ - public function isLastOfMonth() + public function isLastOfMonth(): bool { return $this->day === $this->daysInMonth; } + /** + * Check if the instance is start of a given unit (tolerating a given interval). + * + * @example + * ``` + * // Check if a date-time is the first 15 minutes of the hour it's in + * Carbon::parse('2019-02-28 20:13:00')->isStartOfUnit(Unit::Hour, '15 minutes'); // true + * ``` + */ + public function isStartOfUnit( + Unit $unit, + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + mixed ...$params, + ): bool { + $interval ??= match ($unit) { + Unit::Day, Unit::Hour, Unit::Minute, Unit::Second, Unit::Millisecond, Unit::Microsecond => Unit::Microsecond, + default => Unit::Day, + }; + + $startOfUnit = $this->avoidMutation()->startOf($unit, ...$params); + $startOfUnitDateTime = $startOfUnit->rawFormat('Y-m-d H:i:s.u'); + $maximumDateTime = $startOfUnit + ->add($interval instanceof Unit ? '1 '.$interval->value : $interval) + ->rawFormat('Y-m-d H:i:s.u'); + + if ($maximumDateTime < $startOfUnitDateTime) { + return false; + } + + return $this->rawFormat('Y-m-d H:i:s.u') < $maximumDateTime; + } + + /** + * Check if the instance is end of a given unit (tolerating a given interval). + * + * @example + * ``` + * // Check if a date-time is the last 15 minutes of the hour it's in + * Carbon::parse('2019-02-28 20:13:00')->isEndOfUnit(Unit::Hour, '15 minutes'); // false + * ``` + */ + public function isEndOfUnit( + Unit $unit, + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + mixed ...$params, + ): bool { + $interval ??= match ($unit) { + Unit::Day, Unit::Hour, Unit::Minute, Unit::Second, Unit::Millisecond, Unit::Microsecond => Unit::Microsecond, + default => Unit::Day, + }; + + $endOfUnit = $this->avoidMutation()->endOf($unit, ...$params); + $endOfUnitDateTime = $endOfUnit->rawFormat('Y-m-d H:i:s.u'); + $minimumDateTime = $endOfUnit + ->sub($interval instanceof Unit ? '1 '.$interval->value : $interval) + ->rawFormat('Y-m-d H:i:s.u'); + + if ($minimumDateTime > $endOfUnitDateTime) { + return false; + } + + return $this->rawFormat('Y-m-d H:i:s.u') > $minimumDateTime; + } + + /** + * Determines if the instance is start of millisecond (first microsecond by default but interval can be customized). + */ + public function isStartOfMillisecond( + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + ): bool { + return $this->isStartOfUnit(Unit::Millisecond, $interval); + } + + /** + * Determines if the instance is end of millisecond (last microsecond by default but interval can be customized). + */ + public function isEndOfMillisecond( + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + ): bool { + return $this->isEndOfUnit(Unit::Millisecond, $interval); + } + + /** + * Determines if the instance is start of second (first microsecond by default but interval can be customized). + */ + public function isStartOfSecond( + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + ): bool { + return $this->isStartOfUnit(Unit::Second, $interval); + } + + /** + * Determines if the instance is end of second (last microsecond by default but interval can be customized). + */ + public function isEndOfSecond( + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + ): bool { + return $this->isEndOfUnit(Unit::Second, $interval); + } + + /** + * Determines if the instance is start of minute (first microsecond by default but interval can be customized). + */ + public function isStartOfMinute( + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + ): bool { + return $this->isStartOfUnit(Unit::Minute, $interval); + } + + /** + * Determines if the instance is end of minute (last microsecond by default but interval can be customized). + */ + public function isEndOfMinute( + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + ): bool { + return $this->isEndOfUnit(Unit::Minute, $interval); + } + + /** + * Determines if the instance is start of hour (first microsecond by default but interval can be customized). + */ + public function isStartOfHour( + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + ): bool { + return $this->isStartOfUnit(Unit::Hour, $interval); + } + + /** + * Determines if the instance is end of hour (last microsecond by default but interval can be customized). + */ + public function isEndOfHour( + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + ): bool { + return $this->isEndOfUnit(Unit::Hour, $interval); + } + /** * Check if the instance is start of day / midnight. * @@ -816,12 +896,47 @@ public function isLastOfMonth() * Carbon::parse('2019-02-28 00:00:00.000012')->isStartOfDay(true); // false * ``` * - * @param bool $checkMicroseconds check time at microseconds precision - * - * @return bool + * @param bool $checkMicroseconds check time at microseconds precision + * @param Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval if an interval is specified it will be used as precision + * for instance with "15 minutes", it checks if current date-time + * is in the last 15 minutes of the day, with Unit::Hour, it + * checks if it's in the last hour of the day. */ - public function isStartOfDay($checkMicroseconds = false) - { + public function isStartOfDay( + Unit|DateInterval|Closure|CarbonConverterInterface|string|bool $checkMicroseconds = false, + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + ): bool { + if ($checkMicroseconds === true) { + @trigger_error( + "Since 3.8.0, it's deprecated to use \$checkMicroseconds.\n". + "It will be removed in 4.0.0.\n". + "Instead, you should use either isStartOfDay(interval: Unit::Microsecond) or isStartOfDay(interval: Unit::Second)\n". + 'And you can now use any custom interval as precision, such as isStartOfDay(interval: "15 minutes")', + \E_USER_DEPRECATED, + ); + } + + if ($interval === null && !\is_bool($checkMicroseconds)) { + $interval = $checkMicroseconds; + } + + if ($interval !== null) { + if ($interval instanceof Unit) { + $interval = '1 '.$interval->value; + } + + $date = $this->rawFormat('Y-m-d'); + $time = $this->rawFormat('H:i:s.u'); + $maximum = $this->avoidMutation()->startOfDay()->add($interval); + $maximumDate = $maximum->rawFormat('Y-m-d'); + + if ($date === $maximumDate) { + return $time < $maximum->rawFormat('H:i:s.u'); + } + + return $maximumDate > $date; + } + /* @var CarbonInterface $this */ return $checkMicroseconds ? $this->rawFormat('H:i:s.u') === '00:00:00.000000' @@ -842,18 +957,191 @@ public function isStartOfDay($checkMicroseconds = false) * Carbon::parse('2019-02-28 23:59:59')->isEndOfDay(true); // false * ``` * - * @param bool $checkMicroseconds check time at microseconds precision - * - * @return bool + * @param bool $checkMicroseconds check time at microseconds precision + * @param Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval if an interval is specified it will be used as precision + * for instance with "15 minutes", it checks if current date-time + * is in the last 15 minutes of the day, with Unit::Hour, it + * checks if it's in the last hour of the day. */ - public function isEndOfDay($checkMicroseconds = false) - { + public function isEndOfDay( + Unit|DateInterval|Closure|CarbonConverterInterface|string|bool $checkMicroseconds = false, + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + ): bool { + if ($checkMicroseconds === true) { + @trigger_error( + "Since 3.8.0, it's deprecated to use \$checkMicroseconds.\n". + "It will be removed in 4.0.0.\n". + "Instead, you should use either isEndOfDay(interval: Unit::Microsecond) or isEndOfDay(interval: Unit::Second)\n". + 'And you can now use any custom interval as precision, such as isEndOfDay(interval: "15 minutes")', + \E_USER_DEPRECATED, + ); + } + + if ($interval === null && !\is_bool($checkMicroseconds)) { + $interval = $checkMicroseconds; + } + + if ($interval !== null) { + $date = $this->rawFormat('Y-m-d'); + $time = $this->rawFormat('H:i:s.u'); + $minimum = $this->avoidMutation() + ->endOfDay() + ->sub($interval instanceof Unit ? '1 '.$interval->value : $interval); + $minimumDate = $minimum->rawFormat('Y-m-d'); + + if ($date === $minimumDate) { + return $time > $minimum->rawFormat('H:i:s.u'); + } + + return $minimumDate < $date; + } + /* @var CarbonInterface $this */ return $checkMicroseconds ? $this->rawFormat('H:i:s.u') === '23:59:59.999999' : $this->rawFormat('H:i:s') === '23:59:59'; } + /** + * Determines if the instance is start of week (first day by default but interval can be customized). + * + * @example + * ``` + * Carbon::parse('2024-08-31')->startOfWeek()->isStartOfWeek(); // true + * Carbon::parse('2024-08-31')->isStartOfWeek(); // false + * ``` + */ + public function isStartOfWeek( + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + WeekDay|int|null $weekStartsAt = null, + ): bool { + return $this->isStartOfUnit(Unit::Week, $interval, $weekStartsAt); + } + + /** + * Determines if the instance is end of week (last day by default but interval can be customized). + * + * @example + * ``` + * Carbon::parse('2024-08-31')->endOfWeek()->isEndOfWeek(); // true + * Carbon::parse('2024-08-31')->isEndOfWeek(); // false + * ``` + */ + public function isEndOfWeek( + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + WeekDay|int|null $weekEndsAt = null, + ): bool { + return $this->isEndOfUnit(Unit::Week, $interval, $weekEndsAt); + } + + /** + * Determines if the instance is start of month (first day by default but interval can be customized). + */ + public function isStartOfMonth( + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + ): bool { + return $this->isStartOfUnit(Unit::Month, $interval); + } + + /** + * Determines if the instance is end of month (last day by default but interval can be customized). + */ + public function isEndOfMonth( + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + ): bool { + return $this->isEndOfUnit(Unit::Month, $interval); + } + + /** + * Determines if the instance is start of quarter (first day by default but interval can be customized). + */ + public function isStartOfQuarter( + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + ): bool { + return $this->isStartOfUnit(Unit::Quarter, $interval); + } + + /** + * Determines if the instance is end of quarter (last day by default but interval can be customized). + */ + public function isEndOfQuarter( + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + ): bool { + return $this->isEndOfUnit(Unit::Quarter, $interval); + } + + /** + * Determines if the instance is start of year (first day by default but interval can be customized). + */ + public function isStartOfYear( + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + ): bool { + return $this->isStartOfUnit(Unit::Year, $interval); + } + + /** + * Determines if the instance is end of year (last day by default but interval can be customized). + */ + public function isEndOfYear( + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + ): bool { + return $this->isEndOfUnit(Unit::Year, $interval); + } + + /** + * Determines if the instance is start of decade (first day by default but interval can be customized). + */ + public function isStartOfDecade( + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + ): bool { + return $this->isStartOfUnit(Unit::Decade, $interval); + } + + /** + * Determines if the instance is end of decade (last day by default but interval can be customized). + */ + public function isEndOfDecade( + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + ): bool { + return $this->isEndOfUnit(Unit::Decade, $interval); + } + + /** + * Determines if the instance is start of century (first day by default but interval can be customized). + */ + public function isStartOfCentury( + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + ): bool { + return $this->isStartOfUnit(Unit::Century, $interval); + } + + /** + * Determines if the instance is end of century (last day by default but interval can be customized). + */ + public function isEndOfCentury( + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + ): bool { + return $this->isEndOfUnit(Unit::Century, $interval); + } + + /** + * Determines if the instance is start of millennium (first day by default but interval can be customized). + */ + public function isStartOfMillennium( + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + ): bool { + return $this->isStartOfUnit(Unit::Millennium, $interval); + } + + /** + * Determines if the instance is end of millennium (last day by default but interval can be customized). + */ + public function isEndOfMillennium( + Unit|DateInterval|Closure|CarbonConverterInterface|string|null $interval = null, + ): bool { + return $this->isEndOfUnit(Unit::Millennium, $interval); + } + /** * Check if the instance is start of day / midnight. * @@ -863,10 +1151,8 @@ public function isEndOfDay($checkMicroseconds = false) * Carbon::parse('2019-02-28 00:00:00.999999')->isMidnight(); // true * Carbon::parse('2019-02-28 00:00:01')->isMidnight(); // false * ``` - * - * @return bool */ - public function isMidnight() + public function isMidnight(): bool { return $this->isStartOfDay(); } @@ -881,10 +1167,8 @@ public function isMidnight() * Carbon::parse('2019-02-28 12:00:00.999999')->isMidday(); // true * Carbon::parse('2019-02-28 12:00:01')->isMidday(); // false * ``` - * - * @return bool */ - public function isMidday() + public function isMidday(): bool { /* @var CarbonInterface $this */ return $this->rawFormat('G:i:s') === static::$midDayAt.':00:00'; @@ -898,19 +1182,10 @@ public function isMidday() * Carbon::hasFormat('11:12:45', 'h:i:s'); // true * Carbon::hasFormat('13:12:45', 'h:i:s'); // false * ``` - * - * @param string $date - * @param string $format - * - * @return bool */ - public static function hasFormat($date, $format) + public static function hasFormat(string $date, string $format): bool { - // createFromFormat() is known to handle edge cases silently. - // E.g. "1975-5-1" (Y-n-j) will still be parsed correctly when "Y-m-d" is supplied as the format. - // To ensure we're really testing against our desired format, perform an additional regex validation. - - return self::matchFormatPattern((string) $date, preg_quote((string) $format, '/'), static::$regexFormats); + return FactoryImmutable::getInstance()->hasFormat($date, $format); } /** @@ -927,9 +1202,9 @@ public static function hasFormat($date, $format) * * @return bool */ - public static function hasFormatWithModifiers($date, $format): bool + public static function hasFormatWithModifiers(?string $date, string $format): bool { - return self::matchFormatPattern((string) $date, (string) $format, array_merge(static::$regexFormats, static::$regexFormatModifiers)); + return FactoryImmutable::getInstance()->hasFormatWithModifiers($date, $format); } /** @@ -941,21 +1216,20 @@ public static function hasFormatWithModifiers($date, $format): bool * Carbon::canBeCreatedFromFormat('11:12:45', 'h:i:s'); // true * Carbon::canBeCreatedFromFormat('13:12:45', 'h:i:s'); // false * ``` - * - * @param string $date - * @param string $format - * - * @return bool */ - public static function canBeCreatedFromFormat($date, $format) + public static function canBeCreatedFromFormat(?string $date, string $format): bool { + if ($date === null) { + return false; + } + try { // Try to create a DateTime object. Throws an InvalidArgumentException if the provided time string // doesn't match the format in any way. if (!static::rawCreateFromFormat($format, $date)) { return false; } - } catch (InvalidArgumentException $e) { + } catch (InvalidArgumentException) { return false; } @@ -983,11 +1257,13 @@ public static function canBeCreatedFromFormat($date, $format) * ``` * * @param string $tester day name, month name, hour, date, etc. as string - * - * @return bool */ - public function is(string $tester) + public function is(WeekDay|Month|string $tester): bool { + if ($tester instanceof BackedEnum) { + $tester = $tester->name; + } + $tester = trim($tester); if (preg_match('/^\d+$/', $tester)) { @@ -995,21 +1271,27 @@ public function is(string $tester) } if (preg_match('/^(?:Jan|January|Feb|February|Mar|March|Apr|April|May|Jun|June|Jul|July|Aug|August|Sep|September|Oct|October|Nov|November|Dec|December)$/i', $tester)) { - return $this->isSameMonth(static::parse($tester), false); + return $this->isSameMonth( + $this->transmitFactory(static fn () => static::parse("$tester 1st")), + false, + ); } if (preg_match('/^\d{3,}-\d{1,2}$/', $tester)) { - return $this->isSameMonth(static::parse($tester)); + return $this->isSameMonth( + $this->transmitFactory(static fn () => static::parse($tester)), + ); } - if (preg_match('/^\d{1,2}-\d{1,2}$/', $tester)) { - return $this->isSameDay(static::parse($this->year.'-'.$tester)); + if (preg_match('/^(\d{1,2})-(\d{1,2})$/', $tester, $match)) { + return $this->month === (int) $match[1] && $this->day === (int) $match[2]; } $modifier = preg_replace('/(\d)h$/i', '$1:00', $tester); /* @var CarbonInterface $max */ - $median = static::parse('5555-06-15 12:30:30.555555')->modify($modifier); + $median = $this->transmitFactory(static fn () => static::parse('5555-06-15 12:30:30.555555')) + ->modify($modifier); $current = $this->avoidMutation(); /* @var CarbonInterface $other */ $other = $this->avoidMutation()->modify($modifier); @@ -1032,7 +1314,7 @@ public function is(string $tester) if (preg_match( '/^(?:january|february|march|april|may|june|july|august|september|october|november|december)(?:\s+\d+)?$/i', - $tester + $tester, )) { return $current->startOfMonth()->eq($other->startOfMonth()); } @@ -1057,42 +1339,6 @@ public function is(string $tester) return $current->eq($other); } - /** - * Checks if the (date)time string is in a given format with - * given list of pattern replacements. - * - * @example - * ``` - * Carbon::hasFormat('11:12:45', 'h:i:s'); // true - * Carbon::hasFormat('13:12:45', 'h:i:s'); // false - * ``` - * - * @param string $date - * @param string $format - * @param array $replacements - * - * @return bool - */ - private static function matchFormatPattern(string $date, string $format, array $replacements): bool - { - // Preg quote, but remove escaped backslashes since we'll deal with escaped characters in the format string. - $regex = str_replace('\\\\', '\\', $format); - // Replace not-escaped letters - $regex = preg_replace_callback( - '/(?endOfTime ?? false; } - - private function discourageNull($value): void - { - if ($value === null) { - @trigger_error("Since 2.61.0, it's deprecated to compare a date to null, meaning of such comparison is ambiguous and will no longer be possible in 3.0.0, you should explicitly pass 'now' or make an other check to eliminate null values.", \E_USER_DEPRECATED); - } - } - - private function discourageBoolean($value): void - { - if (\is_bool($value)) { - @trigger_error("Since 2.61.0, it's deprecated to compare a date to true or false, meaning of such comparison is ambiguous and will no longer be possible in 3.0.0, you should explicitly pass 'now' or make an other check to eliminate boolean values.", \E_USER_DEPRECATED); - } - } } diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Converter.php b/vendor/nesbot/carbon/src/Carbon/Traits/Converter.php index fff8a600a..764c5d4f0 100644 --- a/vendor/nesbot/carbon/src/Carbon/Traits/Converter.php +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Converter.php @@ -1,5 +1,7 @@ localFormatFunction ?: static::$formatFunction; + $function = $this->localFormatFunction + ?? $this->getFactory()->getSettings()['formatFunction'] + ?? static::$formatFunction; if (!$function) { return $this->rawFormat($format); @@ -64,12 +63,8 @@ public function format($format) /** * @see https://php.net/manual/en/datetime.format.php - * - * @param string $format - * - * @return string */ - public function rawFormat($format) + public function rawFormat(string $format): string { return parent::format($format); } @@ -81,12 +76,12 @@ public function rawFormat($format) * ``` * echo Carbon::now(); // Carbon instances can be cast to string * ``` - * - * @return string */ - public function __toString() + public function __toString(): string { - $format = $this->localToStringFormat ?? static::$toStringFormat; + $format = $this->localToStringFormat + ?? $this->getFactory()->getSettings()['toStringFormat'] + ?? null; return $format instanceof Closure ? $format($this) @@ -104,10 +99,8 @@ public function __toString() * ``` * echo Carbon::now()->toDateString(); * ``` - * - * @return string */ - public function toDateString() + public function toDateString(): string { return $this->rawFormat('Y-m-d'); } @@ -119,10 +112,8 @@ public function toDateString() * ``` * echo Carbon::now()->toFormattedDateString(); * ``` - * - * @return string */ - public function toFormattedDateString() + public function toFormattedDateString(): string { return $this->rawFormat('M j, Y'); } @@ -134,8 +125,6 @@ public function toFormattedDateString() * ``` * echo Carbon::now()->toFormattedDayDateString(); * ``` - * - * @return string */ public function toFormattedDayDateString(): string { @@ -149,12 +138,8 @@ public function toFormattedDayDateString(): string * ``` * echo Carbon::now()->toTimeString(); * ``` - * - * @param string $unitPrecision - * - * @return string */ - public function toTimeString($unitPrecision = 'second') + public function toTimeString(string $unitPrecision = 'second'): string { return $this->rawFormat(static::getTimeFormatByPrecision($unitPrecision)); } @@ -166,12 +151,8 @@ public function toTimeString($unitPrecision = 'second') * ``` * echo Carbon::now()->toDateTimeString(); * ``` - * - * @param string $unitPrecision - * - * @return string */ - public function toDateTimeString($unitPrecision = 'second') + public function toDateTimeString(string $unitPrecision = 'second'): string { return $this->rawFormat('Y-m-d '.static::getTimeFormatByPrecision($unitPrecision)); } @@ -180,25 +161,16 @@ public function toDateTimeString($unitPrecision = 'second') * Return a format from H:i to H:i:s.u according to given unit precision. * * @param string $unitPrecision "minute", "second", "millisecond" or "microsecond" - * - * @return string - */ - public static function getTimeFormatByPrecision($unitPrecision) - { - switch (static::singularUnit($unitPrecision)) { - case 'minute': - return 'H:i'; - case 'second': - return 'H:i:s'; - case 'm': - case 'millisecond': - return 'H:i:s.v'; - case 'µ': - case 'microsecond': - return 'H:i:s.u'; - } - - throw new UnitException('Precision unit expected among: minute, second, millisecond and microsecond.'); + */ + public static function getTimeFormatByPrecision(string $unitPrecision): string + { + return match (static::singularUnit($unitPrecision)) { + 'minute' => 'H:i', + 'second' => 'H:i:s', + 'm', 'millisecond' => 'H:i:s.v', + 'µ', 'microsecond' => 'H:i:s.u', + default => throw new UnitException('Precision unit expected among: minute, second, millisecond and microsecond.'), + }; } /** @@ -210,12 +182,8 @@ public static function getTimeFormatByPrecision($unitPrecision) * echo "\n"; * echo Carbon::now()->toDateTimeLocalString('minute'); // You can specify precision among: minute, second, millisecond and microsecond * ``` - * - * @param string $unitPrecision - * - * @return string */ - public function toDateTimeLocalString($unitPrecision = 'second') + public function toDateTimeLocalString(string $unitPrecision = 'second'): string { return $this->rawFormat('Y-m-d\T'.static::getTimeFormatByPrecision($unitPrecision)); } @@ -227,10 +195,8 @@ public function toDateTimeLocalString($unitPrecision = 'second') * ``` * echo Carbon::now()->toDayDateTimeString(); * ``` - * - * @return string */ - public function toDayDateTimeString() + public function toDayDateTimeString(): string { return $this->rawFormat('D, M j, Y g:i A'); } @@ -242,10 +208,8 @@ public function toDayDateTimeString() * ``` * echo Carbon::now()->toAtomString(); * ``` - * - * @return string */ - public function toAtomString() + public function toAtomString(): string { return $this->rawFormat(DateTime::ATOM); } @@ -257,12 +221,10 @@ public function toAtomString() * ``` * echo Carbon::now()->toCookieString(); * ``` - * - * @return string */ - public function toCookieString() + public function toCookieString(): string { - return $this->rawFormat(DateTime::COOKIE); + return $this->rawFormat(DateTimeInterface::COOKIE); } /** @@ -272,10 +234,8 @@ public function toCookieString() * ``` * echo Carbon::now()->toIso8601String(); * ``` - * - * @return string */ - public function toIso8601String() + public function toIso8601String(): string { return $this->toAtomString(); } @@ -287,12 +247,10 @@ public function toIso8601String() * ``` * echo Carbon::now()->toRfc822String(); * ``` - * - * @return string */ - public function toRfc822String() + public function toRfc822String(): string { - return $this->rawFormat(DateTime::RFC822); + return $this->rawFormat(DateTimeInterface::RFC822); } /** @@ -302,12 +260,8 @@ public function toRfc822String() * ``` * echo Carbon::now()->toIso8601ZuluString(); * ``` - * - * @param string $unitPrecision - * - * @return string */ - public function toIso8601ZuluString($unitPrecision = 'second') + public function toIso8601ZuluString(string $unitPrecision = 'second'): string { return $this->avoidMutation() ->utc() @@ -321,12 +275,10 @@ public function toIso8601ZuluString($unitPrecision = 'second') * ``` * echo Carbon::now()->toRfc850String(); * ``` - * - * @return string */ - public function toRfc850String() + public function toRfc850String(): string { - return $this->rawFormat(DateTime::RFC850); + return $this->rawFormat(DateTimeInterface::RFC850); } /** @@ -336,12 +288,10 @@ public function toRfc850String() * ``` * echo Carbon::now()->toRfc1036String(); * ``` - * - * @return string */ - public function toRfc1036String() + public function toRfc1036String(): string { - return $this->rawFormat(DateTime::RFC1036); + return $this->rawFormat(DateTimeInterface::RFC1036); } /** @@ -351,12 +301,10 @@ public function toRfc1036String() * ``` * echo Carbon::now()->toRfc1123String(); * ``` - * - * @return string */ - public function toRfc1123String() + public function toRfc1123String(): string { - return $this->rawFormat(DateTime::RFC1123); + return $this->rawFormat(DateTimeInterface::RFC1123); } /** @@ -366,35 +314,24 @@ public function toRfc1123String() * ``` * echo Carbon::now()->toRfc2822String(); * ``` - * - * @return string */ - public function toRfc2822String() + public function toRfc2822String(): string { - return $this->rawFormat(DateTime::RFC2822); + return $this->rawFormat(DateTimeInterface::RFC2822); } /** - * Format the instance as RFC3339 - * - * @param bool $extended + * Format the instance as RFC3339. * * @example * ``` * echo Carbon::now()->toRfc3339String() . "\n"; * echo Carbon::now()->toRfc3339String(true) . "\n"; * ``` - * - * @return string */ - public function toRfc3339String($extended = false) + public function toRfc3339String(bool $extended = false): string { - $format = DateTime::RFC3339; - if ($extended) { - $format = DateTime::RFC3339_EXTENDED; - } - - return $this->rawFormat($format); + return $this->rawFormat($extended ? DateTimeInterface::RFC3339_EXTENDED : DateTimeInterface::RFC3339); } /** @@ -404,12 +341,10 @@ public function toRfc3339String($extended = false) * ``` * echo Carbon::now()->toRssString(); * ``` - * - * @return string */ - public function toRssString() + public function toRssString(): string { - return $this->rawFormat(DateTime::RSS); + return $this->rawFormat(DateTimeInterface::RSS); } /** @@ -419,12 +354,10 @@ public function toRssString() * ``` * echo Carbon::now()->toW3cString(); * ``` - * - * @return string */ - public function toW3cString() + public function toW3cString(): string { - return $this->rawFormat(DateTime::W3C); + return $this->rawFormat(DateTimeInterface::W3C); } /** @@ -434,10 +367,8 @@ public function toW3cString() * ``` * echo Carbon::now()->toRfc7231String(); * ``` - * - * @return string */ - public function toRfc7231String() + public function toRfc7231String(): string { return $this->avoidMutation() ->setTimezone('GMT') @@ -451,10 +382,8 @@ public function toRfc7231String() * ``` * var_dump(Carbon::now()->toArray()); * ``` - * - * @return array */ - public function toArray() + public function toArray(): array { return [ 'year' => $this->year, @@ -479,25 +408,21 @@ public function toArray() * ``` * var_dump(Carbon::now()->toObject()); * ``` - * - * @return object */ - public function toObject() + public function toObject(): object { return (object) $this->toArray(); } /** - * Returns english human readable complete date string. + * Returns english human-readable complete date string. * * @example * ``` * echo Carbon::now()->toString(); * ``` - * - * @return string */ - public function toString() + public function toString(): string { return $this->avoidMutation()->locale('en')->isoFormat('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); } @@ -513,20 +438,18 @@ public function toString() * ``` * * @param bool $keepOffset Pass true to keep the date offset. Else forced to UTC. - * - * @return null|string */ - public function toISOString($keepOffset = false) + public function toISOString(bool $keepOffset = false): ?string { if (!$this->isValid()) { return null; } $yearFormat = $this->year < 0 || $this->year > 9999 ? 'YYYYYY' : 'YYYY'; - $tzFormat = $keepOffset ? 'Z' : '[Z]'; + $timezoneFormat = $keepOffset ? 'Z' : '[Z]'; $date = $keepOffset ? $this : $this->avoidMutation()->utc(); - return $date->isoFormat("$yearFormat-MM-DD[T]HH:mm:ss.SSSSSS$tzFormat"); + return $date->isoFormat("$yearFormat-MM-DD[T]HH:mm:ss.SSSSSS$timezoneFormat"); } /** @@ -536,10 +459,8 @@ public function toISOString($keepOffset = false) * ``` * echo Carbon::now('America/Toronto')->toJSON(); * ``` - * - * @return null|string */ - public function toJSON() + public function toJSON(): ?string { return $this->toISOString(); } @@ -551,12 +472,11 @@ public function toJSON() * ``` * var_dump(Carbon::now()->toDateTime()); * ``` - * - * @return DateTime */ - public function toDateTime() + public function toDateTime(): DateTime { - return new DateTime($this->rawFormat('Y-m-d H:i:s.u'), $this->getTimezone()); + return DateTime::createFromFormat('U.u', $this->rawFormat('U.u')) + ->setTimezone($this->getTimezone()); } /** @@ -566,12 +486,11 @@ public function toDateTime() * ``` * var_dump(Carbon::now()->toDateTimeImmutable()); * ``` - * - * @return DateTimeImmutable */ - public function toDateTimeImmutable() + public function toDateTimeImmutable(): DateTimeImmutable { - return new DateTimeImmutable($this->rawFormat('Y-m-d H:i:s.u'), $this->getTimezone()); + return DateTimeImmutable::createFromFormat('U.u', $this->rawFormat('U.u')) + ->setTimezone($this->getTimezone()); } /** @@ -583,10 +502,8 @@ public function toDateTimeImmutable() * ``` * var_dump(Carbon::now()->toDate()); * ``` - * - * @return DateTime */ - public function toDate() + public function toDate(): DateTime { return $this->toDateTime(); } @@ -597,30 +514,32 @@ public function toDate() * @param \DateTimeInterface|Carbon|CarbonImmutable|int|null $end period end date or recurrences count if int * @param int|\DateInterval|string|null $interval period default interval or number of the given $unit * @param string|null $unit if specified, $interval must be an integer - * - * @return CarbonPeriod */ - public function toPeriod($end = null, $interval = null, $unit = null) + public function toPeriod($end = null, $interval = null, $unit = null): CarbonPeriod { if ($unit) { $interval = CarbonInterval::make("$interval ".static::pluralUnit($unit)); } - $period = ($this->isMutable() ? new CarbonPeriod() : new CarbonPeriodImmutable()) - ->setDateClass(static::class) - ->setStartDate($this); + $isDefaultInterval = !$interval; + $interval ??= CarbonInterval::day(); + $class = $this->isMutable() ? CarbonPeriod::class : CarbonPeriodImmutable::class; - if ($interval) { - $period = $period->setDateInterval($interval); + if (\is_int($end) || (\is_string($end) && ctype_digit($end))) { + $end = (int) $end; } - if (\is_int($end) || (\is_string($end) && ctype_digit($end))) { - $period = $period->setRecurrences($end); - } elseif ($end) { - $period = $period->setEndDate($end); + $end ??= 1; + + if (!\is_int($end)) { + $end = $this->resolveCarbon($end); } - return $period; + return new $class( + raw: [$this, CarbonInterval::make($interval), $end], + dateClass: static::class, + isDefaultInterval: $isDefaultInterval, + ); } /** @@ -629,10 +548,8 @@ public function toPeriod($end = null, $interval = null, $unit = null) * @param \DateTimeInterface|Carbon|CarbonImmutable|null $end period end date * @param int|\DateInterval|string|null $interval period default interval or number of the given $unit * @param string|null $unit if specified, $interval must be an integer - * - * @return CarbonPeriod */ - public function range($end = null, $interval = null, $unit = null) + public function range($end = null, $interval = null, $unit = null): CarbonPeriod { return $this->toPeriod($end, $interval, $unit); } diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Creator.php b/vendor/nesbot/carbon/src/Carbon/Traits/Creator.php index 0d611ea22..9509e5bad 100644 --- a/vendor/nesbot/carbon/src/Carbon/Traits/Creator.php +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Creator.php @@ -1,5 +1,7 @@ constructTimezoneFromDateTime($time, $tz)->format('Y-m-d H:i:s.u'); - } - - if (is_numeric($time) && (!\is_string($time) || !preg_match('/^\d{1,14}$/', $time))) { + public function __construct( + DateTimeInterface|WeekDay|Month|string|int|float|null $time = null, + DateTimeZone|string|int|null $timezone = null, + ) { + $this->initLocalFactory(); + + if ($time instanceof Month) { + $time = $time->name.' 1'; + } elseif ($time instanceof WeekDay) { + $time = $time->name; + } elseif ($time instanceof DateTimeInterface) { + $time = $this->constructTimezoneFromDateTime($time, $timezone)->format('Y-m-d H:i:s.u'); + } + + if (\is_string($time) && str_starts_with($time, '@')) { + $time = static::createFromTimestampUTC(substr($time, 1))->format('Y-m-d\TH:i:s.uP'); + } elseif (is_numeric($time) && (!\is_string($time) || !preg_match('/^\d{1,14}$/', $time))) { $time = static::createFromTimestampUTC($time)->format('Y-m-d\TH:i:s.uP'); } - // If the class has a test now set and we are trying to create a now() + // If the class has a test now set, and we are trying to create a now() // instance then override as required - $isNow = empty($time) || $time === 'now'; - - if (method_exists(static::class, 'hasTestNow') && - method_exists(static::class, 'getTestNow') && - static::hasTestNow() && + $isNow = \in_array($time, [null, '', 'now'], true); + $timezone = static::safeCreateDateTimeZone($timezone) ?? null; + + if ( + ($this->clock || ( + method_exists(static::class, 'hasTestNow') && + method_exists(static::class, 'getTestNow') && + static::hasTestNow() + )) && ($isNow || static::hasRelativeKeywords($time)) ) { - static::mockConstructorParameters($time, $tz); - } - - // Work-around for PHP bug https://bugs.php.net/bug.php?id=67127 - if (!str_contains((string) .1, '.')) { - $locale = setlocale(LC_NUMERIC, '0'); // @codeCoverageIgnore - setlocale(LC_NUMERIC, 'C'); // @codeCoverageIgnore + $this->mockConstructorParameters($time, $timezone); } try { - parent::__construct($time ?: 'now', static::safeCreateDateTimeZone($tz) ?: null); + parent::__construct($time ?? 'now', $timezone); } catch (Exception $exception) { throw new InvalidFormatException($exception->getMessage(), 0, $exception); } $this->constructedObjectId = spl_object_hash($this); - if (isset($locale)) { - setlocale(LC_NUMERIC, $locale); // @codeCoverageIgnore - } - self::setLastErrors(parent::getLastErrors()); } /** * Get timezone from a datetime instance. - * - * @param DateTimeInterface $date - * @param DateTimeZone|string|null $tz - * - * @return DateTimeInterface */ - private function constructTimezoneFromDateTime(DateTimeInterface $date, &$tz) - { - if ($tz !== null) { - $safeTz = static::safeCreateDateTimeZone($tz); + private function constructTimezoneFromDateTime( + DateTimeInterface $date, + DateTimeZone|string|int|null &$timezone, + ): DateTimeInterface { + if ($timezone !== null) { + $safeTz = static::safeCreateDateTimeZone($timezone); if ($safeTz) { - return ($date instanceof DateTimeImmutable ? $date : clone $date)->setTimezone($safeTz); + $date = ($date instanceof DateTimeImmutable ? $date : clone $date)->setTimezone($safeTz); } return $date; } - $tz = $date->getTimezone(); + $timezone = $date->getTimezone(); return $date; } @@ -128,27 +132,22 @@ private function constructTimezoneFromDateTime(DateTimeInterface $date, &$tz) /** * Update constructedObjectId on cloned. */ - public function __clone() + public function __clone(): void { $this->constructedObjectId = spl_object_hash($this); } /** * Create a Carbon instance from a DateTime one. - * - * @param DateTimeInterface $date - * - * @return static */ - public static function instance($date) + public static function instance(DateTimeInterface $date): static { if ($date instanceof static) { return clone $date; } - static::expectDateTime($date); - - $instance = new static($date->format('Y-m-d H:i:s.u'), $date->getTimezone()); + $instance = parent::createFromFormat('U.u', $date->format('U.u')) + ->setTimezone($date->getTimezone()); if ($date instanceof CarbonInterface) { $settings = $date->getSettings(); @@ -170,35 +169,29 @@ public static function instance($date) * as it allows you to do Carbon::parse('Monday next week')->fn() rather * than (new Carbon('Monday next week'))->fn(). * - * @param string|DateTimeInterface|null $time - * @param DateTimeZone|string|null $tz - * * @throws InvalidFormatException - * - * @return static */ - public static function rawParse($time = null, $tz = null) - { + public static function rawParse( + DateTimeInterface|WeekDay|Month|string|int|float|null $time, + DateTimeZone|string|int|null $timezone = null, + ): static { if ($time instanceof DateTimeInterface) { return static::instance($time); } try { - return new static($time, $tz); + return new static($time, $timezone); } catch (Exception $exception) { // @codeCoverageIgnoreStart try { - $date = @static::now($tz)->change($time); - } catch (DateMalformedStringException $ignoredException) { + $date = @static::now($timezone)->change($time); + } catch (DateMalformedStringException|InvalidFormatException) { $date = null; } // @codeCoverageIgnoreEnd - if (!$date) { - throw new InvalidFormatException("Could not parse '$time': ".$exception->getMessage(), 0, $exception); - } - - return $date; + return $date + ?? throw new InvalidFormatException("Could not parse '$time': ".$exception->getMessage(), 0, $exception); } } @@ -209,19 +202,16 @@ public static function rawParse($time = null, $tz = null) * as it allows you to do Carbon::parse('Monday next week')->fn() rather * than (new Carbon('Monday next week'))->fn(). * - * @param string|DateTimeInterface|null $time - * @param DateTimeZone|string|null $tz - * * @throws InvalidFormatException - * - * @return static */ - public static function parse($time = null, $tz = null) - { + public static function parse( + DateTimeInterface|WeekDay|Month|string|int|float|null $time, + DateTimeZone|string|int|null $timezone = null, + ): static { $function = static::$parseFunction; if (!$function) { - return static::rawParse($time, $tz); + return static::rawParse($time, $timezone); } if (\is_string($function) && method_exists(static::class, $function)) { @@ -234,120 +224,75 @@ public static function parse($time = null, $tz = null) /** * Create a carbon instance from a localized string (in French, Japanese, Arabic, etc.). * - * @param string $time date/time string in the given language (may also contain English). - * @param string|null $locale if locale is null or not specified, current global locale will be - * used instead. - * @param DateTimeZone|string|null $tz optional timezone for the new instance. + * @param string $time date/time string in the given language (may also contain English). + * @param string|null $locale if locale is null or not specified, current global locale will be + * used instead. + * @param DateTimeZone|string|int|null $timezone optional timezone for the new instance. * * @throws InvalidFormatException - * - * @return static */ - public static function parseFromLocale($time, $locale = null, $tz = null) - { - return static::rawParse(static::translateTimeString($time, $locale, 'en'), $tz); + public static function parseFromLocale( + string $time, + ?string $locale = null, + DateTimeZone|string|int|null $timezone = null, + ): static { + return static::rawParse(static::translateTimeString($time, $locale, static::DEFAULT_LOCALE), $timezone); } /** * Get a Carbon instance for the current date and time. - * - * @param DateTimeZone|string|null $tz - * - * @return static */ - public static function now($tz = null) + public static function now(DateTimeZone|string|int|null $timezone = null): static { - return new static(null, $tz); + return new static(null, $timezone); } /** * Create a Carbon instance for today. - * - * @param DateTimeZone|string|null $tz - * - * @return static */ - public static function today($tz = null) + public static function today(DateTimeZone|string|int|null $timezone = null): static { - return static::rawParse('today', $tz); + return static::rawParse('today', $timezone); } /** * Create a Carbon instance for tomorrow. - * - * @param DateTimeZone|string|null $tz - * - * @return static */ - public static function tomorrow($tz = null) + public static function tomorrow(DateTimeZone|string|int|null $timezone = null): static { - return static::rawParse('tomorrow', $tz); + return static::rawParse('tomorrow', $timezone); } /** * Create a Carbon instance for yesterday. - * - * @param DateTimeZone|string|null $tz - * - * @return static - */ - public static function yesterday($tz = null) - { - return static::rawParse('yesterday', $tz); - } - - /** - * Create a Carbon instance for the greatest supported date. - * - * @return static */ - public static function maxValue() + public static function yesterday(DateTimeZone|string|int|null $timezone = null): static { - if (self::$PHPIntSize === 4) { - // 32 bit - return static::createFromTimestamp(PHP_INT_MAX); // @codeCoverageIgnore - } - - // 64 bit - return static::create(9999, 12, 31, 23, 59, 59); - } - - /** - * Create a Carbon instance for the lowest supported date. - * - * @return static - */ - public static function minValue() - { - if (self::$PHPIntSize === 4) { - // 32 bit - return static::createFromTimestamp(~PHP_INT_MAX); // @codeCoverageIgnore - } - - // 64 bit - return static::create(1, 1, 1, 0, 0, 0); + return static::rawParse('yesterday', $timezone); } - private static function assertBetween($unit, $value, $min, $max) + private static function assertBetween($unit, $value, $min, $max): void { if (static::isStrictModeEnabled() && ($value < $min || $value > $max)) { throw new OutOfRangeException($unit, $min, $max, $value); } } - private static function createNowInstance($tz) + private static function createNowInstance($timezone) { if (!static::hasTestNow()) { - return static::now($tz); + return static::now($timezone); } $now = static::getTestNow(); if ($now instanceof Closure) { - return $now(static::now($tz)); + return $now(static::now($timezone)); } - return $now->avoidMutation()->tz($tz); + $now = $now->avoidMutation(); + + return $timezone === null ? $now : $now->setTimezone($timezone); } /** @@ -362,28 +307,30 @@ private static function createNowInstance($tz) * If $hour is not null then the default values for $minute and $second * will be 0. * - * @param DateTimeInterface|int|null $year - * @param int|null $month - * @param int|null $day - * @param int|null $hour - * @param int|null $minute - * @param int|null $second - * @param DateTimeZone|string|null $tz + * @param DateTimeInterface|string|int|null $year + * @param int|null $month + * @param int|null $day + * @param int|null $hour + * @param int|null $minute + * @param int|null $second + * @param DateTimeZone|string|int|null $timezone * * @throws InvalidFormatException * - * @return static|false + * @return static|null */ - public static function create($year = 0, $month = 1, $day = 1, $hour = 0, $minute = 0, $second = 0, $tz = null) + public static function create($year = 0, $month = 1, $day = 1, $hour = 0, $minute = 0, $second = 0, $timezone = null): ?static { + $month = self::monthToInt($month); + if ((\is_string($year) && !is_numeric($year)) || $year instanceof DateTimeInterface) { - return static::parse($year, $tz ?: (\is_string($month) || $month instanceof DateTimeZone ? $month : null)); + return static::parse($year, $timezone ?? (\is_string($month) || $month instanceof DateTimeZone ? $month : null)); } $defaults = null; - $getDefault = function ($unit) use ($tz, &$defaults) { + $getDefault = function ($unit) use ($timezone, &$defaults) { if ($defaults === null) { - $now = self::createNowInstance($tz); + $now = self::createNowInstance($timezone); $defaults = array_combine([ 'year', @@ -422,13 +369,13 @@ public static function create($year = 0, $month = 1, $day = 1, $hour = 0, $minut } $second = ($second < 10 ? '0' : '').number_format($second, 6); - $instance = static::rawCreateFromFormat('!Y-n-j G:i:s.u', sprintf('%s-%s-%s %s:%02s:%02s', $year, $month, $day, $hour, $minute, $second), $tz); + $instance = static::rawCreateFromFormat('!Y-n-j G:i:s.u', \sprintf('%s-%s-%s %s:%02s:%02s', $year, $month, $day, $hour, $minute, $second), $timezone); - if ($fixYear !== null) { + if ($instance && $fixYear !== null) { $instance = $instance->addYears($fixYear); } - return $instance; + return $instance ?? null; } /** @@ -446,20 +393,21 @@ public static function create($year = 0, $month = 1, $day = 1, $hour = 0, $minut * If one of the set values is not valid, an InvalidDateException * will be thrown. * - * @param int|null $year - * @param int|null $month - * @param int|null $day - * @param int|null $hour - * @param int|null $minute - * @param int|null $second - * @param DateTimeZone|string|null $tz + * @param int|null $year + * @param int|null $month + * @param int|null $day + * @param int|null $hour + * @param int|null $minute + * @param int|null $second + * @param DateTimeZone|string|int|null $timezone * * @throws InvalidDateException * - * @return static|false + * @return static|null */ - public static function createSafe($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $tz = null) + public static function createSafe($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $timezone = null): ?static { + $month = self::monthToInt($month); $fields = static::getRangesByUnit(); foreach ($fields as $field => $range) { @@ -468,11 +416,11 @@ public static function createSafe($year = null, $month = null, $day = null, $hou throw new InvalidDateException($field, $$field); } - return false; + return null; } } - $instance = static::create($year, $month, $day, $hour, $minute, $second, $tz); + $instance = static::create($year, $month, $day, $hour, $minute, $second, $timezone); foreach (array_reverse($fields) as $field => $range) { if ($$field !== null && (!\is_int($$field) || $$field !== $instance->$field)) { @@ -480,7 +428,7 @@ public static function createSafe($year = null, $month = null, $day = null, $hou throw new InvalidDateException($field, $$field); } - return false; + return null; } } @@ -492,25 +440,25 @@ public static function createSafe($year = null, $month = null, $day = null, $hou * * @see create() * - * @param int|null $year - * @param int|null $month - * @param int|null $day - * @param int|null $hour - * @param int|null $minute - * @param int|null $second - * @param DateTimeZone|string|null $tz + * @param int|null $year + * @param int|null $month + * @param int|null $day + * @param int|null $hour + * @param int|null $minute + * @param int|null $second + * @param DateTimeZone|string|int|null $timezone * * @throws InvalidFormatException * * @return static */ - public static function createStrict(?int $year = 0, ?int $month = 1, ?int $day = 1, ?int $hour = 0, ?int $minute = 0, ?int $second = 0, $tz = null): self + public static function createStrict(?int $year = 0, ?int $month = 1, ?int $day = 1, ?int $hour = 0, ?int $minute = 0, ?int $second = 0, $timezone = null): static { $initialStrictMode = static::isStrictModeEnabled(); static::useStrictMode(true); try { - $date = static::create($year, $month, $day, $hour, $minute, $second, $tz); + $date = static::create($year, $month, $day, $hour, $minute, $second, $timezone); } finally { static::useStrictMode($initialStrictMode); } @@ -521,114 +469,101 @@ public static function createStrict(?int $year = 0, ?int $month = 1, ?int $day = /** * Create a Carbon instance from just a date. The time portion is set to now. * - * @param int|null $year - * @param int|null $month - * @param int|null $day - * @param DateTimeZone|string|null $tz + * @param int|null $year + * @param int|null $month + * @param int|null $day + * @param DateTimeZone|string|int|null $timezone * * @throws InvalidFormatException * * @return static */ - public static function createFromDate($year = null, $month = null, $day = null, $tz = null) + public static function createFromDate($year = null, $month = null, $day = null, $timezone = null) { - return static::create($year, $month, $day, null, null, null, $tz); + return static::create($year, $month, $day, null, null, null, $timezone); } /** * Create a Carbon instance from just a date. The time portion is set to midnight. * - * @param int|null $year - * @param int|null $month - * @param int|null $day - * @param DateTimeZone|string|null $tz + * @param int|null $year + * @param int|null $month + * @param int|null $day + * @param DateTimeZone|string|int|null $timezone * * @throws InvalidFormatException * * @return static */ - public static function createMidnightDate($year = null, $month = null, $day = null, $tz = null) + public static function createMidnightDate($year = null, $month = null, $day = null, $timezone = null) { - return static::create($year, $month, $day, 0, 0, 0, $tz); + return static::create($year, $month, $day, 0, 0, 0, $timezone); } /** * Create a Carbon instance from just a time. The date portion is set to today. * - * @param int|null $hour - * @param int|null $minute - * @param int|null $second - * @param DateTimeZone|string|null $tz + * @param int|null $hour + * @param int|null $minute + * @param int|null $second + * @param DateTimeZone|string|int|null $timezone * * @throws InvalidFormatException * * @return static */ - public static function createFromTime($hour = 0, $minute = 0, $second = 0, $tz = null) + public static function createFromTime($hour = 0, $minute = 0, $second = 0, $timezone = null): static { - return static::create(null, null, null, $hour, $minute, $second, $tz); + return static::create(null, null, null, $hour, $minute, $second, $timezone); } /** * Create a Carbon instance from a time string. The date portion is set to today. * - * @param string $time - * @param DateTimeZone|string|null $tz - * * @throws InvalidFormatException - * - * @return static */ - public static function createFromTimeString($time, $tz = null) + public static function createFromTimeString(string $time, DateTimeZone|string|int|null $timezone = null): static { - return static::today($tz)->setTimeFromTimeString($time); + return static::today($timezone)->setTimeFromTimeString($time); } - /** - * @param string $format Datetime format - * @param string $time - * @param DateTimeZone|string|false|null $originalTz - * - * @return DateTimeInterface|false - */ - private static function createFromFormatAndTimezone($format, $time, $originalTz) - { - // Work-around for https://bugs.php.net/bug.php?id=75577 - // @codeCoverageIgnoreStart - if (version_compare(PHP_VERSION, '7.3.0-dev', '<')) { - $format = str_replace('.v', '.u', $format); + private static function createFromFormatAndTimezone( + string $format, + string $time, + DateTimeZone|string|int|null $originalTimezone, + ): ?DateTimeInterface { + if ($originalTimezone === null) { + return parent::createFromFormat($format, $time) ?: null; } - // @codeCoverageIgnoreEnd - if ($originalTz === null) { - return parent::createFromFormat($format, (string) $time); - } + $timezone = \is_int($originalTimezone) ? self::getOffsetTimezone($originalTimezone) : $originalTimezone; - $tz = \is_int($originalTz) - ? @timezone_name_from_abbr('', (int) ($originalTz * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE), 1) - : $originalTz; + $timezone = static::safeCreateDateTimeZone($timezone, $originalTimezone); - $tz = static::safeCreateDateTimeZone($tz, $originalTz); + return parent::createFromFormat($format, $time, $timezone) ?: null; + } - if ($tz === false) { - return false; - } + private static function getOffsetTimezone(int $offset): string + { + $minutes = (int) ($offset * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE); - return parent::createFromFormat($format, (string) $time, $tz); + return @timezone_name_from_abbr('', $minutes, 1) ?: throw new InvalidTimeZoneException( + "Invalid offset timezone $offset", + ); } /** * Create a Carbon instance from a specific format. * - * @param string $format Datetime format - * @param string $time - * @param DateTimeZone|string|false|null $tz + * @param string $format Datetime format + * @param string $time + * @param DateTimeZone|string|int|null $timezone * * @throws InvalidFormatException * - * @return static|false + * @return static|null */ - public static function rawCreateFromFormat($format, $time, $tz = null) + public static function rawCreateFromFormat(string $format, string $time, $timezone = null): ?static { // Work-around for https://bugs.php.net/bug.php?id=80141 $format = preg_replace('/(?getTimezone(); + if ($timezone === null && !preg_match("/{$nonEscaped}[eOPT]/", $nonIgnored)) { + $timezone = clone $mock->getTimezone(); } $mock = $mock->copy(); @@ -676,7 +611,7 @@ public static function rawCreateFromFormat($format, $time, $tz = null) } // Regenerate date from the modified format to base result on the mocked instance instead of now. - $date = self::createFromFormatAndTimezone($format, $time, $tz); + $date = self::createFromFormatAndTimezone($format, $time, $timezone); } if ($date instanceof DateTimeInterface) { @@ -687,30 +622,43 @@ public static function rawCreateFromFormat($format, $time, $tz = null) } if (static::isStrictModeEnabled()) { - throw new InvalidFormatException(implode(PHP_EOL, $lastErrors['errors'])); + throw new InvalidFormatException(implode(PHP_EOL, (array) $lastErrors['errors'])); } - return false; + return null; } /** * Create a Carbon instance from a specific format. * - * @param string $format Datetime format - * @param string $time - * @param DateTimeZone|string|false|null $tz + * @param string $format Datetime format + * @param string $time + * @param DateTimeZone|string|int|null $timezone * * @throws InvalidFormatException * - * @return static|false + * @return static|null */ #[ReturnTypeWillChange] - public static function createFromFormat($format, $time, $tz = null) + public static function createFromFormat($format, $time, $timezone = null): ?static { $function = static::$createFromFormatFunction; + // format is a single numeric unit + if (\is_int($time) && \in_array(ltrim($format, '!'), ['U', 'Y', 'y', 'X', 'x', 'm', 'n', 'd', 'j', 'w', 'W', 'H', 'h', 'G', 'g', 'i', 's', 'u', 'z', 'v'], true)) { + $time = (string) $time; + } + + if (!\is_string($time)) { + @trigger_error( + 'createFromFormat() $time parameter will only accept string or integer for 1-letter format representing a numeric unit in the next version', + \E_USER_DEPRECATED, + ); + $time = (string) $time; + } + if (!$function) { - return static::rawCreateFromFormat($format, $time, $tz); + return static::rawCreateFromFormat($format, $time, $timezone); } if (\is_string($function) && method_exists(static::class, $function)) { @@ -723,42 +671,45 @@ public static function createFromFormat($format, $time, $tz = null) /** * Create a Carbon instance from a specific ISO format (same replacements as ->isoFormat()). * - * @param string $format Datetime format - * @param string $time - * @param DateTimeZone|string|false|null $tz optional timezone - * @param string|null $locale locale to be used for LTS, LT, LL, LLL, etc. macro-formats (en by fault, unneeded if no such macro-format in use) - * @param \Symfony\Component\Translation\TranslatorInterface $translator optional custom translator to use for macro-formats + * @param string $format Datetime format + * @param string $time + * @param DateTimeZone|string|int|null $timezone optional timezone + * @param string|null $locale locale to be used for LTS, LT, LL, LLL, etc. macro-formats (en by fault, unneeded if no such macro-format in use) + * @param TranslatorInterface|null $translator optional custom translator to use for macro-formats * * @throws InvalidFormatException * - * @return static|false + * @return static|null */ - public static function createFromIsoFormat($format, $time, $tz = null, $locale = 'en', $translator = null) - { + public static function createFromIsoFormat( + string $format, + string $time, + $timezone = null, + ?string $locale = CarbonInterface::DEFAULT_LOCALE, + ?TranslatorInterface $translator = null + ): ?static { $format = preg_replace_callback('/(? static::getTranslationMessageWith($translator, 'formats.LT', $locale, 'h:mm A'), - 'LTS' => static::getTranslationMessageWith($translator, 'formats.LTS', $locale, 'h:mm:ss A'), - 'L' => static::getTranslationMessageWith($translator, 'formats.L', $locale, 'MM/DD/YYYY'), - 'LL' => static::getTranslationMessageWith($translator, 'formats.LL', $locale, 'MMMM D, YYYY'), - 'LLL' => static::getTranslationMessageWith($translator, 'formats.LLL', $locale, 'MMMM D, YYYY h:mm A'), - 'LLLL' => static::getTranslationMessageWith($translator, 'formats.LLLL', $locale, 'dddd, MMMM D, YYYY h:mm A'), + 'LT' => static::getTranslationMessageWith($translator, 'formats.LT', $locale), + 'LTS' => static::getTranslationMessageWith($translator, 'formats.LTS', $locale), + 'L' => static::getTranslationMessageWith($translator, 'formats.L', $locale), + 'LL' => static::getTranslationMessageWith($translator, 'formats.LL', $locale), + 'LLL' => static::getTranslationMessageWith($translator, 'formats.LLL', $locale), + 'LLLL' => static::getTranslationMessageWith($translator, 'formats.LLLL', $locale), ]; } return $formats[$code] ?? preg_replace_callback( '/MMMM|MM|DD|dddd/', - function ($code) { - return mb_substr($code[0], 1); - }, - $formats[strtoupper($code)] ?? '' + static fn (array $code) => mb_substr($code[0], 1), + $formats[strtoupper($code)] ?? '', ); }, $format); @@ -859,28 +810,28 @@ function ($code) { return $format; }, $format); - return static::rawCreateFromFormat($format, $time, $tz); + return static::rawCreateFromFormat($format, $time, $timezone); } /** * Create a Carbon instance from a specific format and a string in a given language. * - * @param string $format Datetime format - * @param string $locale - * @param string $time - * @param DateTimeZone|string|false|null $tz + * @param string $format Datetime format + * @param string $locale + * @param string $time + * @param DateTimeZone|string|int|null $timezone * * @throws InvalidFormatException * - * @return static|false + * @return static|null */ - public static function createFromLocaleFormat($format, $locale, $time, $tz = null) + public static function createFromLocaleFormat(string $format, string $locale, string $time, $timezone = null): ?static { $format = preg_replace_callback( '/(?:\\\\[a-zA-Z]|[bfkqCEJKQRV]){2,}/', static function (array $match) use ($locale): string { $word = str_replace('\\', '', $match[0]); - $translatedWord = static::translateTimeString($word, $locale, 'en'); + $translatedWord = static::translateTimeString($word, $locale, static::DEFAULT_LOCALE); return $word === $translatedWord ? $match[0] @@ -889,26 +840,26 @@ static function (array $match) use ($locale): string { $format ); - return static::rawCreateFromFormat($format, static::translateTimeString($time, $locale, 'en'), $tz); + return static::rawCreateFromFormat($format, static::translateTimeString($time, $locale, static::DEFAULT_LOCALE), $timezone); } /** * Create a Carbon instance from a specific ISO format and a string in a given language. * - * @param string $format Datetime ISO format - * @param string $locale - * @param string $time - * @param DateTimeZone|string|false|null $tz + * @param string $format Datetime ISO format + * @param string $locale + * @param string $time + * @param DateTimeZone|string|int|null $timezone * * @throws InvalidFormatException * - * @return static|false + * @return static|null */ - public static function createFromLocaleIsoFormat($format, $locale, $time, $tz = null) + public static function createFromLocaleIsoFormat(string $format, string $locale, string $time, $timezone = null): ?static { - $time = static::translateTimeString($time, $locale, 'en', CarbonInterface::TRANSLATE_MONTHS | CarbonInterface::TRANSLATE_DAYS | CarbonInterface::TRANSLATE_MERIDIEM); + $time = static::translateTimeString($time, $locale, static::DEFAULT_LOCALE, CarbonInterface::TRANSLATE_MONTHS | CarbonInterface::TRANSLATE_DAYS | CarbonInterface::TRANSLATE_MERIDIEM); - return static::createFromIsoFormat($format, $time, $tz, $locale); + return static::createFromIsoFormat($format, $time, $timezone, $locale); } /** @@ -923,7 +874,7 @@ public static function createFromLocaleIsoFormat($format, $locale, $time, $tz = * * @return static|null */ - public static function make($var) + public static function make($var, DateTimeZone|string|null $timezone = null): ?static { if ($var instanceof DateTimeInterface) { return static::instance($var); @@ -938,7 +889,7 @@ public static function make($var) !preg_match('/^R\d/', $var) && preg_match('/[a-z\d]/i', $var) ) { - $date = static::parse($var); + $date = static::parse($var, $timezone); } } @@ -952,7 +903,7 @@ public static function make($var) * * @return void */ - private static function setLastErrors($lastErrors) + private static function setLastErrors($lastErrors): void { if (\is_array($lastErrors) || $lastErrors === false) { static::$lastErrors = \is_array($lastErrors) ? $lastErrors : [ @@ -966,12 +917,22 @@ private static function setLastErrors($lastErrors) /** * {@inheritdoc} - * - * @return array */ - #[ReturnTypeWillChange] - public static function getLastErrors() + public static function getLastErrors(): array|false + { + return static::$lastErrors ?? false; + } + + private static function monthToInt(mixed $value, string $unit = 'month'): mixed { - return static::$lastErrors; + if ($value instanceof Month) { + if ($unit !== 'month') { + throw new UnitException("Month enum cannot be used to set $unit"); + } + + return Month::int($value); + } + + return $value; } } diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Date.php b/vendor/nesbot/carbon/src/Carbon/Traits/Date.php index 8ae5c1781..14b9abafa 100644 --- a/vendor/nesbot/carbon/src/Carbon/Traits/Date.php +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Date.php @@ -1,5 +1,7 @@ * + * @property string $localeDayOfWeek the day of week in current locale + * @property string $shortLocaleDayOfWeek the abbreviated day of week in current locale + * @property string $localeMonth the month in current locale + * @property string $shortLocaleMonth the abbreviated month in current locale * @property int $year * @property int $yearIso * @property int $month @@ -52,6 +62,7 @@ * @property int $second * @property int $micro * @property int $microsecond + * @property int $dayOfWeekIso 1 (for Monday) through 7 (for Sunday) * @property int|float|string $timestamp seconds since the Unix Epoch * @property string $englishDayOfWeek the day of week in English * @property string $shortEnglishDayOfWeek the abbreviated day of week in English @@ -64,17 +75,90 @@ * @property int $isoWeek 1 through 53 * @property int $weekYear year according to week format * @property int $isoWeekYear year according to ISO week format - * @property int $dayOfYear 1 through 366 * @property int $age does a diffInYears() with default parameters * @property int $offset the timezone offset in seconds from UTC * @property int $offsetMinutes the timezone offset in minutes from UTC * @property int $offsetHours the timezone offset in hours from UTC * @property CarbonTimeZone $timezone the current timezone * @property CarbonTimeZone $tz alias of $timezone - * @property-read int $dayOfWeek 0 (for Sunday) through 6 (for Saturday) - * @property-read int $dayOfWeekIso 1 (for Monday) through 7 (for Sunday) - * @property-read int $weekOfYear ISO-8601 week number of year, weeks starting on Monday - * @property-read int $daysInMonth number of days in the given month + * @property int $centuryOfMillennium The value of the century starting from the beginning of the current millennium + * @property int $dayOfCentury The value of the day starting from the beginning of the current century + * @property int $dayOfDecade The value of the day starting from the beginning of the current decade + * @property int $dayOfMillennium The value of the day starting from the beginning of the current millennium + * @property int $dayOfMonth The value of the day starting from the beginning of the current month + * @property int $dayOfQuarter The value of the day starting from the beginning of the current quarter + * @property int $dayOfWeek 0 (for Sunday) through 6 (for Saturday) + * @property int $dayOfYear 1 through 366 + * @property int $decadeOfCentury The value of the decade starting from the beginning of the current century + * @property int $decadeOfMillennium The value of the decade starting from the beginning of the current millennium + * @property int $hourOfCentury The value of the hour starting from the beginning of the current century + * @property int $hourOfDay The value of the hour starting from the beginning of the current day + * @property int $hourOfDecade The value of the hour starting from the beginning of the current decade + * @property int $hourOfMillennium The value of the hour starting from the beginning of the current millennium + * @property int $hourOfMonth The value of the hour starting from the beginning of the current month + * @property int $hourOfQuarter The value of the hour starting from the beginning of the current quarter + * @property int $hourOfWeek The value of the hour starting from the beginning of the current week + * @property int $hourOfYear The value of the hour starting from the beginning of the current year + * @property int $microsecondOfCentury The value of the microsecond starting from the beginning of the current century + * @property int $microsecondOfDay The value of the microsecond starting from the beginning of the current day + * @property int $microsecondOfDecade The value of the microsecond starting from the beginning of the current decade + * @property int $microsecondOfHour The value of the microsecond starting from the beginning of the current hour + * @property int $microsecondOfMillennium The value of the microsecond starting from the beginning of the current millennium + * @property int $microsecondOfMillisecond The value of the microsecond starting from the beginning of the current millisecond + * @property int $microsecondOfMinute The value of the microsecond starting from the beginning of the current minute + * @property int $microsecondOfMonth The value of the microsecond starting from the beginning of the current month + * @property int $microsecondOfQuarter The value of the microsecond starting from the beginning of the current quarter + * @property int $microsecondOfSecond The value of the microsecond starting from the beginning of the current second + * @property int $microsecondOfWeek The value of the microsecond starting from the beginning of the current week + * @property int $microsecondOfYear The value of the microsecond starting from the beginning of the current year + * @property int $millisecondOfCentury The value of the millisecond starting from the beginning of the current century + * @property int $millisecondOfDay The value of the millisecond starting from the beginning of the current day + * @property int $millisecondOfDecade The value of the millisecond starting from the beginning of the current decade + * @property int $millisecondOfHour The value of the millisecond starting from the beginning of the current hour + * @property int $millisecondOfMillennium The value of the millisecond starting from the beginning of the current millennium + * @property int $millisecondOfMinute The value of the millisecond starting from the beginning of the current minute + * @property int $millisecondOfMonth The value of the millisecond starting from the beginning of the current month + * @property int $millisecondOfQuarter The value of the millisecond starting from the beginning of the current quarter + * @property int $millisecondOfSecond The value of the millisecond starting from the beginning of the current second + * @property int $millisecondOfWeek The value of the millisecond starting from the beginning of the current week + * @property int $millisecondOfYear The value of the millisecond starting from the beginning of the current year + * @property int $minuteOfCentury The value of the minute starting from the beginning of the current century + * @property int $minuteOfDay The value of the minute starting from the beginning of the current day + * @property int $minuteOfDecade The value of the minute starting from the beginning of the current decade + * @property int $minuteOfHour The value of the minute starting from the beginning of the current hour + * @property int $minuteOfMillennium The value of the minute starting from the beginning of the current millennium + * @property int $minuteOfMonth The value of the minute starting from the beginning of the current month + * @property int $minuteOfQuarter The value of the minute starting from the beginning of the current quarter + * @property int $minuteOfWeek The value of the minute starting from the beginning of the current week + * @property int $minuteOfYear The value of the minute starting from the beginning of the current year + * @property int $monthOfCentury The value of the month starting from the beginning of the current century + * @property int $monthOfDecade The value of the month starting from the beginning of the current decade + * @property int $monthOfMillennium The value of the month starting from the beginning of the current millennium + * @property int $monthOfQuarter The value of the month starting from the beginning of the current quarter + * @property int $monthOfYear The value of the month starting from the beginning of the current year + * @property int $quarterOfCentury The value of the quarter starting from the beginning of the current century + * @property int $quarterOfDecade The value of the quarter starting from the beginning of the current decade + * @property int $quarterOfMillennium The value of the quarter starting from the beginning of the current millennium + * @property int $quarterOfYear The value of the quarter starting from the beginning of the current year + * @property int $secondOfCentury The value of the second starting from the beginning of the current century + * @property int $secondOfDay The value of the second starting from the beginning of the current day + * @property int $secondOfDecade The value of the second starting from the beginning of the current decade + * @property int $secondOfHour The value of the second starting from the beginning of the current hour + * @property int $secondOfMillennium The value of the second starting from the beginning of the current millennium + * @property int $secondOfMinute The value of the second starting from the beginning of the current minute + * @property int $secondOfMonth The value of the second starting from the beginning of the current month + * @property int $secondOfQuarter The value of the second starting from the beginning of the current quarter + * @property int $secondOfWeek The value of the second starting from the beginning of the current week + * @property int $secondOfYear The value of the second starting from the beginning of the current year + * @property int $weekOfCentury The value of the week starting from the beginning of the current century + * @property int $weekOfDecade The value of the week starting from the beginning of the current decade + * @property int $weekOfMillennium The value of the week starting from the beginning of the current millennium + * @property int $weekOfMonth 1 through 5 + * @property int $weekOfQuarter The value of the week starting from the beginning of the current quarter + * @property int $weekOfYear ISO-8601 week number of year, weeks starting on Monday + * @property int $yearOfCentury The value of the year starting from the beginning of the current century + * @property int $yearOfDecade The value of the year starting from the beginning of the current decade + * @property int $yearOfMillennium The value of the year starting from the beginning of the current millennium * @property-read string $latinMeridiem "am"/"pm" (Ante meridiem or Post meridiem latin lowercase mark) * @property-read string $latinUpperMeridiem "AM"/"PM" (Ante meridiem or Post meridiem latin uppercase mark) * @property-read string $timezoneAbbreviatedName the current timezone abbreviated name @@ -87,13 +171,10 @@ * @property-read string $meridiem lowercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language * @property-read string $upperMeridiem uppercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language * @property-read int $noZeroHour current hour from 1 to 24 - * @property-read int $weeksInYear 51 through 53 * @property-read int $isoWeeksInYear 51 through 53 - * @property-read int $weekOfMonth 1 through 5 * @property-read int $weekNumberInMonth 1 through 5 * @property-read int $firstWeekDay 0 through 6 * @property-read int $lastWeekDay 0 through 6 - * @property-read int $daysInYear 365 or 366 * @property-read int $quarter the quarter of this instance, 1 - 4 * @property-read int $decade the decade of this instance * @property-read int $century the century of this instance @@ -104,6 +185,84 @@ * @property-read string $timezoneName the current timezone name * @property-read string $tzName alias of $timezoneName * @property-read string $locale locale of the current instance + * @property-read int $centuriesInMillennium The number of centuries contained in the current millennium + * @property-read int $daysInCentury The number of days contained in the current century + * @property-read int $daysInDecade The number of days contained in the current decade + * @property-read int $daysInMillennium The number of days contained in the current millennium + * @property-read int $daysInMonth number of days in the given month + * @property-read int $daysInQuarter The number of days contained in the current quarter + * @property-read int $daysInWeek The number of days contained in the current week + * @property-read int $daysInYear 365 or 366 + * @property-read int $decadesInCentury The number of decades contained in the current century + * @property-read int $decadesInMillennium The number of decades contained in the current millennium + * @property-read int $hoursInCentury The number of hours contained in the current century + * @property-read int $hoursInDay The number of hours contained in the current day + * @property-read int $hoursInDecade The number of hours contained in the current decade + * @property-read int $hoursInMillennium The number of hours contained in the current millennium + * @property-read int $hoursInMonth The number of hours contained in the current month + * @property-read int $hoursInQuarter The number of hours contained in the current quarter + * @property-read int $hoursInWeek The number of hours contained in the current week + * @property-read int $hoursInYear The number of hours contained in the current year + * @property-read int $microsecondsInCentury The number of microseconds contained in the current century + * @property-read int $microsecondsInDay The number of microseconds contained in the current day + * @property-read int $microsecondsInDecade The number of microseconds contained in the current decade + * @property-read int $microsecondsInHour The number of microseconds contained in the current hour + * @property-read int $microsecondsInMillennium The number of microseconds contained in the current millennium + * @property-read int $microsecondsInMillisecond The number of microseconds contained in the current millisecond + * @property-read int $microsecondsInMinute The number of microseconds contained in the current minute + * @property-read int $microsecondsInMonth The number of microseconds contained in the current month + * @property-read int $microsecondsInQuarter The number of microseconds contained in the current quarter + * @property-read int $microsecondsInSecond The number of microseconds contained in the current second + * @property-read int $microsecondsInWeek The number of microseconds contained in the current week + * @property-read int $microsecondsInYear The number of microseconds contained in the current year + * @property-read int $millisecondsInCentury The number of milliseconds contained in the current century + * @property-read int $millisecondsInDay The number of milliseconds contained in the current day + * @property-read int $millisecondsInDecade The number of milliseconds contained in the current decade + * @property-read int $millisecondsInHour The number of milliseconds contained in the current hour + * @property-read int $millisecondsInMillennium The number of milliseconds contained in the current millennium + * @property-read int $millisecondsInMinute The number of milliseconds contained in the current minute + * @property-read int $millisecondsInMonth The number of milliseconds contained in the current month + * @property-read int $millisecondsInQuarter The number of milliseconds contained in the current quarter + * @property-read int $millisecondsInSecond The number of milliseconds contained in the current second + * @property-read int $millisecondsInWeek The number of milliseconds contained in the current week + * @property-read int $millisecondsInYear The number of milliseconds contained in the current year + * @property-read int $minutesInCentury The number of minutes contained in the current century + * @property-read int $minutesInDay The number of minutes contained in the current day + * @property-read int $minutesInDecade The number of minutes contained in the current decade + * @property-read int $minutesInHour The number of minutes contained in the current hour + * @property-read int $minutesInMillennium The number of minutes contained in the current millennium + * @property-read int $minutesInMonth The number of minutes contained in the current month + * @property-read int $minutesInQuarter The number of minutes contained in the current quarter + * @property-read int $minutesInWeek The number of minutes contained in the current week + * @property-read int $minutesInYear The number of minutes contained in the current year + * @property-read int $monthsInCentury The number of months contained in the current century + * @property-read int $monthsInDecade The number of months contained in the current decade + * @property-read int $monthsInMillennium The number of months contained in the current millennium + * @property-read int $monthsInQuarter The number of months contained in the current quarter + * @property-read int $monthsInYear The number of months contained in the current year + * @property-read int $quartersInCentury The number of quarters contained in the current century + * @property-read int $quartersInDecade The number of quarters contained in the current decade + * @property-read int $quartersInMillennium The number of quarters contained in the current millennium + * @property-read int $quartersInYear The number of quarters contained in the current year + * @property-read int $secondsInCentury The number of seconds contained in the current century + * @property-read int $secondsInDay The number of seconds contained in the current day + * @property-read int $secondsInDecade The number of seconds contained in the current decade + * @property-read int $secondsInHour The number of seconds contained in the current hour + * @property-read int $secondsInMillennium The number of seconds contained in the current millennium + * @property-read int $secondsInMinute The number of seconds contained in the current minute + * @property-read int $secondsInMonth The number of seconds contained in the current month + * @property-read int $secondsInQuarter The number of seconds contained in the current quarter + * @property-read int $secondsInWeek The number of seconds contained in the current week + * @property-read int $secondsInYear The number of seconds contained in the current year + * @property-read int $weeksInCentury The number of weeks contained in the current century + * @property-read int $weeksInDecade The number of weeks contained in the current decade + * @property-read int $weeksInMillennium The number of weeks contained in the current millennium + * @property-read int $weeksInMonth The number of weeks contained in the current month + * @property-read int $weeksInQuarter The number of weeks contained in the current quarter + * @property-read int $weeksInYear 51 through 53 + * @property-read int $yearsInCentury The number of years contained in the current century + * @property-read int $yearsInDecade The number of years contained in the current decade + * @property-read int $yearsInMillennium The number of years contained in the current millennium * * @method bool isUtc() Check if the current instance has UTC timezone. (Both isUtc and isUTC cases are valid.) * @method bool isLocal() Check if the current instance has non-UTC timezone. @@ -116,64 +275,72 @@ * @method bool isThursday() Checks if the instance day is thursday. * @method bool isFriday() Checks if the instance day is friday. * @method bool isSaturday() Checks if the instance day is saturday. - * @method bool isSameYear(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same year as the instance. If null passed, compare to now (with the same timezone). + * @method bool isSameYear(DateTimeInterface|string $date) Checks if the given date is in the same year as the instance. If null passed, compare to now (with the same timezone). * @method bool isCurrentYear() Checks if the instance is in the same year as the current moment. * @method bool isNextYear() Checks if the instance is in the same year as the current moment next year. * @method bool isLastYear() Checks if the instance is in the same year as the current moment last year. - * @method bool isSameWeek(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same week as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMonth() Checks if the instance is in the same month as the current moment. + * @method bool isNextMonth() Checks if the instance is in the same month as the current moment next month. + * @method bool isLastMonth() Checks if the instance is in the same month as the current moment last month. + * @method bool isSameWeek(DateTimeInterface|string $date) Checks if the given date is in the same week as the instance. If null passed, compare to now (with the same timezone). * @method bool isCurrentWeek() Checks if the instance is in the same week as the current moment. * @method bool isNextWeek() Checks if the instance is in the same week as the current moment next week. * @method bool isLastWeek() Checks if the instance is in the same week as the current moment last week. - * @method bool isSameDay(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same day as the instance. If null passed, compare to now (with the same timezone). + * @method bool isSameDay(DateTimeInterface|string $date) Checks if the given date is in the same day as the instance. If null passed, compare to now (with the same timezone). * @method bool isCurrentDay() Checks if the instance is in the same day as the current moment. * @method bool isNextDay() Checks if the instance is in the same day as the current moment next day. * @method bool isLastDay() Checks if the instance is in the same day as the current moment last day. - * @method bool isSameHour(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same hour as the instance. If null passed, compare to now (with the same timezone). + * @method bool isSameHour(DateTimeInterface|string $date) Checks if the given date is in the same hour as the instance. If null passed, compare to now (with the same timezone). * @method bool isCurrentHour() Checks if the instance is in the same hour as the current moment. * @method bool isNextHour() Checks if the instance is in the same hour as the current moment next hour. * @method bool isLastHour() Checks if the instance is in the same hour as the current moment last hour. - * @method bool isSameMinute(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same minute as the instance. If null passed, compare to now (with the same timezone). + * @method bool isSameMinute(DateTimeInterface|string $date) Checks if the given date is in the same minute as the instance. If null passed, compare to now (with the same timezone). * @method bool isCurrentMinute() Checks if the instance is in the same minute as the current moment. * @method bool isNextMinute() Checks if the instance is in the same minute as the current moment next minute. * @method bool isLastMinute() Checks if the instance is in the same minute as the current moment last minute. - * @method bool isSameSecond(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same second as the instance. If null passed, compare to now (with the same timezone). + * @method bool isSameSecond(DateTimeInterface|string $date) Checks if the given date is in the same second as the instance. If null passed, compare to now (with the same timezone). * @method bool isCurrentSecond() Checks if the instance is in the same second as the current moment. * @method bool isNextSecond() Checks if the instance is in the same second as the current moment next second. * @method bool isLastSecond() Checks if the instance is in the same second as the current moment last second. - * @method bool isSameMicro(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). + * @method bool isSameMilli(DateTimeInterface|string $date) Checks if the given date is in the same millisecond as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMilli() Checks if the instance is in the same millisecond as the current moment. + * @method bool isNextMilli() Checks if the instance is in the same millisecond as the current moment next millisecond. + * @method bool isLastMilli() Checks if the instance is in the same millisecond as the current moment last millisecond. + * @method bool isSameMillisecond(DateTimeInterface|string $date) Checks if the given date is in the same millisecond as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMillisecond() Checks if the instance is in the same millisecond as the current moment. + * @method bool isNextMillisecond() Checks if the instance is in the same millisecond as the current moment next millisecond. + * @method bool isLastMillisecond() Checks if the instance is in the same millisecond as the current moment last millisecond. + * @method bool isSameMicro(DateTimeInterface|string $date) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). * @method bool isCurrentMicro() Checks if the instance is in the same microsecond as the current moment. * @method bool isNextMicro() Checks if the instance is in the same microsecond as the current moment next microsecond. * @method bool isLastMicro() Checks if the instance is in the same microsecond as the current moment last microsecond. - * @method bool isSameMicrosecond(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). + * @method bool isSameMicrosecond(DateTimeInterface|string $date) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). * @method bool isCurrentMicrosecond() Checks if the instance is in the same microsecond as the current moment. * @method bool isNextMicrosecond() Checks if the instance is in the same microsecond as the current moment next microsecond. * @method bool isLastMicrosecond() Checks if the instance is in the same microsecond as the current moment last microsecond. - * @method bool isCurrentMonth() Checks if the instance is in the same month as the current moment. - * @method bool isNextMonth() Checks if the instance is in the same month as the current moment next month. - * @method bool isLastMonth() Checks if the instance is in the same month as the current moment last month. - * @method bool isCurrentQuarter() Checks if the instance is in the same quarter as the current moment. - * @method bool isNextQuarter() Checks if the instance is in the same quarter as the current moment next quarter. - * @method bool isLastQuarter() Checks if the instance is in the same quarter as the current moment last quarter. - * @method bool isSameDecade(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same decade as the instance. If null passed, compare to now (with the same timezone). + * @method bool isSameDecade(DateTimeInterface|string $date) Checks if the given date is in the same decade as the instance. If null passed, compare to now (with the same timezone). * @method bool isCurrentDecade() Checks if the instance is in the same decade as the current moment. * @method bool isNextDecade() Checks if the instance is in the same decade as the current moment next decade. * @method bool isLastDecade() Checks if the instance is in the same decade as the current moment last decade. - * @method bool isSameCentury(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same century as the instance. If null passed, compare to now (with the same timezone). + * @method bool isSameCentury(DateTimeInterface|string $date) Checks if the given date is in the same century as the instance. If null passed, compare to now (with the same timezone). * @method bool isCurrentCentury() Checks if the instance is in the same century as the current moment. * @method bool isNextCentury() Checks if the instance is in the same century as the current moment next century. * @method bool isLastCentury() Checks if the instance is in the same century as the current moment last century. - * @method bool isSameMillennium(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same millennium as the instance. If null passed, compare to now (with the same timezone). + * @method bool isSameMillennium(DateTimeInterface|string $date) Checks if the given date is in the same millennium as the instance. If null passed, compare to now (with the same timezone). * @method bool isCurrentMillennium() Checks if the instance is in the same millennium as the current moment. * @method bool isNextMillennium() Checks if the instance is in the same millennium as the current moment next millennium. * @method bool isLastMillennium() Checks if the instance is in the same millennium as the current moment last millennium. + * @method bool isCurrentQuarter() Checks if the instance is in the same quarter as the current moment. + * @method bool isNextQuarter() Checks if the instance is in the same quarter as the current moment next quarter. + * @method bool isLastQuarter() Checks if the instance is in the same quarter as the current moment last quarter. * @method CarbonInterface years(int $value) Set current instance year to the given value. * @method CarbonInterface year(int $value) Set current instance year to the given value. * @method CarbonInterface setYears(int $value) Set current instance year to the given value. * @method CarbonInterface setYear(int $value) Set current instance year to the given value. - * @method CarbonInterface months(int $value) Set current instance month to the given value. - * @method CarbonInterface month(int $value) Set current instance month to the given value. - * @method CarbonInterface setMonths(int $value) Set current instance month to the given value. - * @method CarbonInterface setMonth(int $value) Set current instance month to the given value. + * @method CarbonInterface months(Month|int $value) Set current instance month to the given value. + * @method CarbonInterface month(Month|int $value) Set current instance month to the given value. + * @method CarbonInterface setMonths(Month|int $value) Set current instance month to the given value. + * @method CarbonInterface setMonth(Month|int $value) Set current instance month to the given value. * @method CarbonInterface days(int $value) Set current instance day to the given value. * @method CarbonInterface day(int $value) Set current instance day to the given value. * @method CarbonInterface setDays(int $value) Set current instance day to the given value. @@ -206,241 +373,256 @@ * @method CarbonInterface microsecond(int $value) Set current instance microsecond to the given value. * @method CarbonInterface setMicroseconds(int $value) Set current instance microsecond to the given value. * @method CarbonInterface setMicrosecond(int $value) Set current instance microsecond to the given value. - * @method CarbonInterface addYears(int $value = 1) Add years (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addYears(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addYear() Add one year to the instance (using date interval). - * @method CarbonInterface subYears(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subYears(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subYear() Sub one year to the instance (using date interval). - * @method CarbonInterface addYearsWithOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addYearsWithOverflow(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. * @method CarbonInterface addYearWithOverflow() Add one year to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonInterface subYearsWithOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subYearsWithOverflow(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. * @method CarbonInterface subYearWithOverflow() Sub one year to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonInterface addYearsWithoutOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addYearsWithoutOverflow(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addYearWithoutOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subYearsWithoutOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subYearsWithoutOverflow(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subYearWithoutOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addYearsWithNoOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addYearsWithNoOverflow(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addYearWithNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subYearsWithNoOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subYearsWithNoOverflow(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subYearWithNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addYearsNoOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addYearsNoOverflow(int|float $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addYearNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subYearsNoOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subYearsNoOverflow(int|float $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subYearNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addMonths(int $value = 1) Add months (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMonths(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addMonth() Add one month to the instance (using date interval). - * @method CarbonInterface subMonths(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMonths(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subMonth() Sub one month to the instance (using date interval). - * @method CarbonInterface addMonthsWithOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addMonthsWithOverflow(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. * @method CarbonInterface addMonthWithOverflow() Add one month to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonInterface subMonthsWithOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subMonthsWithOverflow(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. * @method CarbonInterface subMonthWithOverflow() Sub one month to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonInterface addMonthsWithoutOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMonthsWithoutOverflow(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addMonthWithoutOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subMonthsWithoutOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMonthsWithoutOverflow(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subMonthWithoutOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addMonthsWithNoOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMonthsWithNoOverflow(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addMonthWithNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subMonthsWithNoOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMonthsWithNoOverflow(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subMonthWithNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addMonthsNoOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMonthsNoOverflow(int|float $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addMonthNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subMonthsNoOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMonthsNoOverflow(int|float $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subMonthNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addDays(int $value = 1) Add days (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addDays(int|float $value = 1) Add days (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addDay() Add one day to the instance (using date interval). - * @method CarbonInterface subDays(int $value = 1) Sub days (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subDays(int|float $value = 1) Sub days (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subDay() Sub one day to the instance (using date interval). - * @method CarbonInterface addHours(int $value = 1) Add hours (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addHours(int|float $value = 1) Add hours (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addHour() Add one hour to the instance (using date interval). - * @method CarbonInterface subHours(int $value = 1) Sub hours (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subHours(int|float $value = 1) Sub hours (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subHour() Sub one hour to the instance (using date interval). - * @method CarbonInterface addMinutes(int $value = 1) Add minutes (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMinutes(int|float $value = 1) Add minutes (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addMinute() Add one minute to the instance (using date interval). - * @method CarbonInterface subMinutes(int $value = 1) Sub minutes (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMinutes(int|float $value = 1) Sub minutes (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subMinute() Sub one minute to the instance (using date interval). - * @method CarbonInterface addSeconds(int $value = 1) Add seconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addSeconds(int|float $value = 1) Add seconds (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addSecond() Add one second to the instance (using date interval). - * @method CarbonInterface subSeconds(int $value = 1) Sub seconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subSeconds(int|float $value = 1) Sub seconds (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subSecond() Sub one second to the instance (using date interval). - * @method CarbonInterface addMillis(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMillis(int|float $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addMilli() Add one millisecond to the instance (using date interval). - * @method CarbonInterface subMillis(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMillis(int|float $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subMilli() Sub one millisecond to the instance (using date interval). - * @method CarbonInterface addMilliseconds(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMilliseconds(int|float $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addMillisecond() Add one millisecond to the instance (using date interval). - * @method CarbonInterface subMilliseconds(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMilliseconds(int|float $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subMillisecond() Sub one millisecond to the instance (using date interval). - * @method CarbonInterface addMicros(int $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMicros(int|float $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addMicro() Add one microsecond to the instance (using date interval). - * @method CarbonInterface subMicros(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMicros(int|float $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subMicro() Sub one microsecond to the instance (using date interval). - * @method CarbonInterface addMicroseconds(int $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMicroseconds(int|float $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addMicrosecond() Add one microsecond to the instance (using date interval). - * @method CarbonInterface subMicroseconds(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMicroseconds(int|float $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subMicrosecond() Sub one microsecond to the instance (using date interval). - * @method CarbonInterface addMillennia(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMillennia(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addMillennium() Add one millennium to the instance (using date interval). - * @method CarbonInterface subMillennia(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMillennia(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subMillennium() Sub one millennium to the instance (using date interval). - * @method CarbonInterface addMillenniaWithOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addMillenniaWithOverflow(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. * @method CarbonInterface addMillenniumWithOverflow() Add one millennium to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonInterface subMillenniaWithOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subMillenniaWithOverflow(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. * @method CarbonInterface subMillenniumWithOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonInterface addMillenniaWithoutOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMillenniaWithoutOverflow(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addMillenniumWithoutOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subMillenniaWithoutOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMillenniaWithoutOverflow(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subMillenniumWithoutOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addMillenniaWithNoOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMillenniaWithNoOverflow(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addMillenniumWithNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subMillenniaWithNoOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMillenniaWithNoOverflow(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subMillenniumWithNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addMillenniaNoOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMillenniaNoOverflow(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addMillenniumNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subMillenniaNoOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMillenniaNoOverflow(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subMillenniumNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addCenturies(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addCenturies(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addCentury() Add one century to the instance (using date interval). - * @method CarbonInterface subCenturies(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subCenturies(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subCentury() Sub one century to the instance (using date interval). - * @method CarbonInterface addCenturiesWithOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addCenturiesWithOverflow(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. * @method CarbonInterface addCenturyWithOverflow() Add one century to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonInterface subCenturiesWithOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subCenturiesWithOverflow(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. * @method CarbonInterface subCenturyWithOverflow() Sub one century to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonInterface addCenturiesWithoutOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addCenturiesWithoutOverflow(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addCenturyWithoutOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subCenturiesWithoutOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subCenturiesWithoutOverflow(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subCenturyWithoutOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addCenturiesWithNoOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addCenturiesWithNoOverflow(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addCenturyWithNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subCenturiesWithNoOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subCenturiesWithNoOverflow(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subCenturyWithNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addCenturiesNoOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addCenturiesNoOverflow(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addCenturyNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subCenturiesNoOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subCenturiesNoOverflow(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subCenturyNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addDecades(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addDecades(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addDecade() Add one decade to the instance (using date interval). - * @method CarbonInterface subDecades(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subDecades(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subDecade() Sub one decade to the instance (using date interval). - * @method CarbonInterface addDecadesWithOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addDecadesWithOverflow(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. * @method CarbonInterface addDecadeWithOverflow() Add one decade to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonInterface subDecadesWithOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subDecadesWithOverflow(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. * @method CarbonInterface subDecadeWithOverflow() Sub one decade to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonInterface addDecadesWithoutOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addDecadesWithoutOverflow(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addDecadeWithoutOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subDecadesWithoutOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subDecadesWithoutOverflow(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subDecadeWithoutOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addDecadesWithNoOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addDecadesWithNoOverflow(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addDecadeWithNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subDecadesWithNoOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subDecadesWithNoOverflow(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subDecadeWithNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addDecadesNoOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addDecadesNoOverflow(int|float $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addDecadeNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subDecadesNoOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subDecadesNoOverflow(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subDecadeNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addQuarters(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addQuarters(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addQuarter() Add one quarter to the instance (using date interval). - * @method CarbonInterface subQuarters(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subQuarters(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subQuarter() Sub one quarter to the instance (using date interval). - * @method CarbonInterface addQuartersWithOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addQuartersWithOverflow(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. * @method CarbonInterface addQuarterWithOverflow() Add one quarter to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonInterface subQuartersWithOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subQuartersWithOverflow(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. * @method CarbonInterface subQuarterWithOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly allowed. - * @method CarbonInterface addQuartersWithoutOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addQuartersWithoutOverflow(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addQuarterWithoutOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subQuartersWithoutOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subQuartersWithoutOverflow(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subQuarterWithoutOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addQuartersWithNoOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addQuartersWithNoOverflow(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addQuarterWithNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subQuartersWithNoOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subQuartersWithNoOverflow(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subQuarterWithNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addQuartersNoOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addQuartersNoOverflow(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface addQuarterNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface subQuartersNoOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subQuartersNoOverflow(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. * @method CarbonInterface subQuarterNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. - * @method CarbonInterface addWeeks(int $value = 1) Add weeks (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addWeeks(int|float $value = 1) Add weeks (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addWeek() Add one week to the instance (using date interval). - * @method CarbonInterface subWeeks(int $value = 1) Sub weeks (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subWeeks(int|float $value = 1) Sub weeks (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subWeek() Sub one week to the instance (using date interval). - * @method CarbonInterface addWeekdays(int $value = 1) Add weekdays (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addWeekdays(int|float $value = 1) Add weekdays (the $value count passed in) to the instance (using date interval). * @method CarbonInterface addWeekday() Add one weekday to the instance (using date interval). - * @method CarbonInterface subWeekdays(int $value = 1) Sub weekdays (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subWeekdays(int|float $value = 1) Sub weekdays (the $value count passed in) to the instance (using date interval). * @method CarbonInterface subWeekday() Sub one weekday to the instance (using date interval). - * @method CarbonInterface addRealMicros(int $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealMicro() Add one microsecond to the instance (using timestamp). - * @method CarbonInterface subRealMicros(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealMicro() Sub one microsecond to the instance (using timestamp). - * @method CarbonPeriod microsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. - * @method CarbonInterface addRealMicroseconds(int $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealMicrosecond() Add one microsecond to the instance (using timestamp). - * @method CarbonInterface subRealMicroseconds(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealMicrosecond() Sub one microsecond to the instance (using timestamp). - * @method CarbonPeriod microsecondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. - * @method CarbonInterface addRealMillis(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealMilli() Add one millisecond to the instance (using timestamp). - * @method CarbonInterface subRealMillis(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealMilli() Sub one millisecond to the instance (using timestamp). - * @method CarbonPeriod millisUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. - * @method CarbonInterface addRealMilliseconds(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealMillisecond() Add one millisecond to the instance (using timestamp). - * @method CarbonInterface subRealMilliseconds(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealMillisecond() Sub one millisecond to the instance (using timestamp). - * @method CarbonPeriod millisecondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. - * @method CarbonInterface addRealSeconds(int $value = 1) Add seconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealSecond() Add one second to the instance (using timestamp). - * @method CarbonInterface subRealSeconds(int $value = 1) Sub seconds (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealSecond() Sub one second to the instance (using timestamp). - * @method CarbonPeriod secondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each second or every X seconds if a factor is given. - * @method CarbonInterface addRealMinutes(int $value = 1) Add minutes (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealMinute() Add one minute to the instance (using timestamp). - * @method CarbonInterface subRealMinutes(int $value = 1) Sub minutes (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealMinute() Sub one minute to the instance (using timestamp). - * @method CarbonPeriod minutesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each minute or every X minutes if a factor is given. - * @method CarbonInterface addRealHours(int $value = 1) Add hours (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealHour() Add one hour to the instance (using timestamp). - * @method CarbonInterface subRealHours(int $value = 1) Sub hours (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealHour() Sub one hour to the instance (using timestamp). - * @method CarbonPeriod hoursUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each hour or every X hours if a factor is given. - * @method CarbonInterface addRealDays(int $value = 1) Add days (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealDay() Add one day to the instance (using timestamp). - * @method CarbonInterface subRealDays(int $value = 1) Sub days (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealDay() Sub one day to the instance (using timestamp). - * @method CarbonPeriod daysUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each day or every X days if a factor is given. - * @method CarbonInterface addRealWeeks(int $value = 1) Add weeks (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealWeek() Add one week to the instance (using timestamp). - * @method CarbonInterface subRealWeeks(int $value = 1) Sub weeks (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealWeek() Sub one week to the instance (using timestamp). - * @method CarbonPeriod weeksUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each week or every X weeks if a factor is given. - * @method CarbonInterface addRealMonths(int $value = 1) Add months (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealMonth() Add one month to the instance (using timestamp). - * @method CarbonInterface subRealMonths(int $value = 1) Sub months (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealMonth() Sub one month to the instance (using timestamp). - * @method CarbonPeriod monthsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each month or every X months if a factor is given. - * @method CarbonInterface addRealQuarters(int $value = 1) Add quarters (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealQuarter() Add one quarter to the instance (using timestamp). - * @method CarbonInterface subRealQuarters(int $value = 1) Sub quarters (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealQuarter() Sub one quarter to the instance (using timestamp). - * @method CarbonPeriod quartersUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each quarter or every X quarters if a factor is given. - * @method CarbonInterface addRealYears(int $value = 1) Add years (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealYear() Add one year to the instance (using timestamp). - * @method CarbonInterface subRealYears(int $value = 1) Sub years (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealYear() Sub one year to the instance (using timestamp). - * @method CarbonPeriod yearsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each year or every X years if a factor is given. - * @method CarbonInterface addRealDecades(int $value = 1) Add decades (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealDecade() Add one decade to the instance (using timestamp). - * @method CarbonInterface subRealDecades(int $value = 1) Sub decades (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealDecade() Sub one decade to the instance (using timestamp). - * @method CarbonPeriod decadesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each decade or every X decades if a factor is given. - * @method CarbonInterface addRealCenturies(int $value = 1) Add centuries (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealCentury() Add one century to the instance (using timestamp). - * @method CarbonInterface subRealCenturies(int $value = 1) Sub centuries (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealCentury() Sub one century to the instance (using timestamp). - * @method CarbonPeriod centuriesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each century or every X centuries if a factor is given. - * @method CarbonInterface addRealMillennia(int $value = 1) Add millennia (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface addRealMillennium() Add one millennium to the instance (using timestamp). - * @method CarbonInterface subRealMillennia(int $value = 1) Sub millennia (the $value count passed in) to the instance (using timestamp). - * @method CarbonInterface subRealMillennium() Sub one millennium to the instance (using timestamp). - * @method CarbonPeriod millenniaUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millennium or every X millennia if a factor is given. + * @method CarbonInterface addUTCMicros(int|float $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCMicro() Add one microsecond to the instance (using timestamp). + * @method CarbonInterface subUTCMicros(int|float $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCMicro() Sub one microsecond to the instance (using timestamp). + * @method CarbonPeriod microsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. + * @method float diffInUTCMicros(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of microseconds. + * @method CarbonInterface addUTCMicroseconds(int|float $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCMicrosecond() Add one microsecond to the instance (using timestamp). + * @method CarbonInterface subUTCMicroseconds(int|float $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCMicrosecond() Sub one microsecond to the instance (using timestamp). + * @method CarbonPeriod microsecondsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. + * @method float diffInUTCMicroseconds(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of microseconds. + * @method CarbonInterface addUTCMillis(int|float $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCMilli() Add one millisecond to the instance (using timestamp). + * @method CarbonInterface subUTCMillis(int|float $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCMilli() Sub one millisecond to the instance (using timestamp). + * @method CarbonPeriod millisUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. + * @method float diffInUTCMillis(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of milliseconds. + * @method CarbonInterface addUTCMilliseconds(int|float $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCMillisecond() Add one millisecond to the instance (using timestamp). + * @method CarbonInterface subUTCMilliseconds(int|float $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCMillisecond() Sub one millisecond to the instance (using timestamp). + * @method CarbonPeriod millisecondsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. + * @method float diffInUTCMilliseconds(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of milliseconds. + * @method CarbonInterface addUTCSeconds(int|float $value = 1) Add seconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCSecond() Add one second to the instance (using timestamp). + * @method CarbonInterface subUTCSeconds(int|float $value = 1) Sub seconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCSecond() Sub one second to the instance (using timestamp). + * @method CarbonPeriod secondsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each second or every X seconds if a factor is given. + * @method float diffInUTCSeconds(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of seconds. + * @method CarbonInterface addUTCMinutes(int|float $value = 1) Add minutes (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCMinute() Add one minute to the instance (using timestamp). + * @method CarbonInterface subUTCMinutes(int|float $value = 1) Sub minutes (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCMinute() Sub one minute to the instance (using timestamp). + * @method CarbonPeriod minutesUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each minute or every X minutes if a factor is given. + * @method float diffInUTCMinutes(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of minutes. + * @method CarbonInterface addUTCHours(int|float $value = 1) Add hours (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCHour() Add one hour to the instance (using timestamp). + * @method CarbonInterface subUTCHours(int|float $value = 1) Sub hours (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCHour() Sub one hour to the instance (using timestamp). + * @method CarbonPeriod hoursUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each hour or every X hours if a factor is given. + * @method float diffInUTCHours(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of hours. + * @method CarbonInterface addUTCDays(int|float $value = 1) Add days (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCDay() Add one day to the instance (using timestamp). + * @method CarbonInterface subUTCDays(int|float $value = 1) Sub days (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCDay() Sub one day to the instance (using timestamp). + * @method CarbonPeriod daysUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each day or every X days if a factor is given. + * @method float diffInUTCDays(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of days. + * @method CarbonInterface addUTCWeeks(int|float $value = 1) Add weeks (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCWeek() Add one week to the instance (using timestamp). + * @method CarbonInterface subUTCWeeks(int|float $value = 1) Sub weeks (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCWeek() Sub one week to the instance (using timestamp). + * @method CarbonPeriod weeksUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each week or every X weeks if a factor is given. + * @method float diffInUTCWeeks(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of weeks. + * @method CarbonInterface addUTCMonths(int|float $value = 1) Add months (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCMonth() Add one month to the instance (using timestamp). + * @method CarbonInterface subUTCMonths(int|float $value = 1) Sub months (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCMonth() Sub one month to the instance (using timestamp). + * @method CarbonPeriod monthsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each month or every X months if a factor is given. + * @method float diffInUTCMonths(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of months. + * @method CarbonInterface addUTCQuarters(int|float $value = 1) Add quarters (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCQuarter() Add one quarter to the instance (using timestamp). + * @method CarbonInterface subUTCQuarters(int|float $value = 1) Sub quarters (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCQuarter() Sub one quarter to the instance (using timestamp). + * @method CarbonPeriod quartersUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each quarter or every X quarters if a factor is given. + * @method float diffInUTCQuarters(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of quarters. + * @method CarbonInterface addUTCYears(int|float $value = 1) Add years (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCYear() Add one year to the instance (using timestamp). + * @method CarbonInterface subUTCYears(int|float $value = 1) Sub years (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCYear() Sub one year to the instance (using timestamp). + * @method CarbonPeriod yearsUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each year or every X years if a factor is given. + * @method float diffInUTCYears(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of years. + * @method CarbonInterface addUTCDecades(int|float $value = 1) Add decades (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCDecade() Add one decade to the instance (using timestamp). + * @method CarbonInterface subUTCDecades(int|float $value = 1) Sub decades (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCDecade() Sub one decade to the instance (using timestamp). + * @method CarbonPeriod decadesUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each decade or every X decades if a factor is given. + * @method float diffInUTCDecades(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of decades. + * @method CarbonInterface addUTCCenturies(int|float $value = 1) Add centuries (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCCentury() Add one century to the instance (using timestamp). + * @method CarbonInterface subUTCCenturies(int|float $value = 1) Sub centuries (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCCentury() Sub one century to the instance (using timestamp). + * @method CarbonPeriod centuriesUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each century or every X centuries if a factor is given. + * @method float diffInUTCCenturies(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of centuries. + * @method CarbonInterface addUTCMillennia(int|float $value = 1) Add millennia (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addUTCMillennium() Add one millennium to the instance (using timestamp). + * @method CarbonInterface subUTCMillennia(int|float $value = 1) Sub millennia (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subUTCMillennium() Sub one millennium to the instance (using timestamp). + * @method CarbonPeriod millenniaUntil($endDate = null, int|float $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millennium or every X millennia if a factor is given. + * @method float diffInUTCMillennia(DateTimeInterface|string|null $date, bool $absolute = false) Convert current and given date in UTC timezone and return a floating number of millennia. * @method CarbonInterface roundYear(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. * @method CarbonInterface roundYears(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. * @method CarbonInterface floorYear(float $precision = 1) Truncate the current instance year with given precision. @@ -521,6 +703,160 @@ * @method string longRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) * @method string shortRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) * @method string longRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method int centuriesInMillennium() Return the number of centuries contained in the current millennium + * @method int|static centuryOfMillennium(?int $century = null) Return the value of the century starting from the beginning of the current millennium when called with no parameters, change the current century when called with an integer value + * @method int|static dayOfCentury(?int $day = null) Return the value of the day starting from the beginning of the current century when called with no parameters, change the current day when called with an integer value + * @method int|static dayOfDecade(?int $day = null) Return the value of the day starting from the beginning of the current decade when called with no parameters, change the current day when called with an integer value + * @method int|static dayOfMillennium(?int $day = null) Return the value of the day starting from the beginning of the current millennium when called with no parameters, change the current day when called with an integer value + * @method int|static dayOfMonth(?int $day = null) Return the value of the day starting from the beginning of the current month when called with no parameters, change the current day when called with an integer value + * @method int|static dayOfQuarter(?int $day = null) Return the value of the day starting from the beginning of the current quarter when called with no parameters, change the current day when called with an integer value + * @method int|static dayOfWeek(?int $day = null) Return the value of the day starting from the beginning of the current week when called with no parameters, change the current day when called with an integer value + * @method int daysInCentury() Return the number of days contained in the current century + * @method int daysInDecade() Return the number of days contained in the current decade + * @method int daysInMillennium() Return the number of days contained in the current millennium + * @method int daysInMonth() Return the number of days contained in the current month + * @method int daysInQuarter() Return the number of days contained in the current quarter + * @method int daysInWeek() Return the number of days contained in the current week + * @method int daysInYear() Return the number of days contained in the current year + * @method int|static decadeOfCentury(?int $decade = null) Return the value of the decade starting from the beginning of the current century when called with no parameters, change the current decade when called with an integer value + * @method int|static decadeOfMillennium(?int $decade = null) Return the value of the decade starting from the beginning of the current millennium when called with no parameters, change the current decade when called with an integer value + * @method int decadesInCentury() Return the number of decades contained in the current century + * @method int decadesInMillennium() Return the number of decades contained in the current millennium + * @method int|static hourOfCentury(?int $hour = null) Return the value of the hour starting from the beginning of the current century when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfDay(?int $hour = null) Return the value of the hour starting from the beginning of the current day when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfDecade(?int $hour = null) Return the value of the hour starting from the beginning of the current decade when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfMillennium(?int $hour = null) Return the value of the hour starting from the beginning of the current millennium when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfMonth(?int $hour = null) Return the value of the hour starting from the beginning of the current month when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfQuarter(?int $hour = null) Return the value of the hour starting from the beginning of the current quarter when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfWeek(?int $hour = null) Return the value of the hour starting from the beginning of the current week when called with no parameters, change the current hour when called with an integer value + * @method int|static hourOfYear(?int $hour = null) Return the value of the hour starting from the beginning of the current year when called with no parameters, change the current hour when called with an integer value + * @method int hoursInCentury() Return the number of hours contained in the current century + * @method int hoursInDay() Return the number of hours contained in the current day + * @method int hoursInDecade() Return the number of hours contained in the current decade + * @method int hoursInMillennium() Return the number of hours contained in the current millennium + * @method int hoursInMonth() Return the number of hours contained in the current month + * @method int hoursInQuarter() Return the number of hours contained in the current quarter + * @method int hoursInWeek() Return the number of hours contained in the current week + * @method int hoursInYear() Return the number of hours contained in the current year + * @method int|static microsecondOfCentury(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current century when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfDay(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current day when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfDecade(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current decade when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfHour(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current hour when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfMillennium(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current millennium when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfMillisecond(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current millisecond when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfMinute(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current minute when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfMonth(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current month when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfQuarter(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current quarter when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfSecond(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current second when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfWeek(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current week when called with no parameters, change the current microsecond when called with an integer value + * @method int|static microsecondOfYear(?int $microsecond = null) Return the value of the microsecond starting from the beginning of the current year when called with no parameters, change the current microsecond when called with an integer value + * @method int microsecondsInCentury() Return the number of microseconds contained in the current century + * @method int microsecondsInDay() Return the number of microseconds contained in the current day + * @method int microsecondsInDecade() Return the number of microseconds contained in the current decade + * @method int microsecondsInHour() Return the number of microseconds contained in the current hour + * @method int microsecondsInMillennium() Return the number of microseconds contained in the current millennium + * @method int microsecondsInMillisecond() Return the number of microseconds contained in the current millisecond + * @method int microsecondsInMinute() Return the number of microseconds contained in the current minute + * @method int microsecondsInMonth() Return the number of microseconds contained in the current month + * @method int microsecondsInQuarter() Return the number of microseconds contained in the current quarter + * @method int microsecondsInSecond() Return the number of microseconds contained in the current second + * @method int microsecondsInWeek() Return the number of microseconds contained in the current week + * @method int microsecondsInYear() Return the number of microseconds contained in the current year + * @method int|static millisecondOfCentury(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current century when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfDay(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current day when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfDecade(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current decade when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfHour(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current hour when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfMillennium(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current millennium when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfMinute(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current minute when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfMonth(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current month when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfQuarter(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current quarter when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfSecond(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current second when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfWeek(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current week when called with no parameters, change the current millisecond when called with an integer value + * @method int|static millisecondOfYear(?int $millisecond = null) Return the value of the millisecond starting from the beginning of the current year when called with no parameters, change the current millisecond when called with an integer value + * @method int millisecondsInCentury() Return the number of milliseconds contained in the current century + * @method int millisecondsInDay() Return the number of milliseconds contained in the current day + * @method int millisecondsInDecade() Return the number of milliseconds contained in the current decade + * @method int millisecondsInHour() Return the number of milliseconds contained in the current hour + * @method int millisecondsInMillennium() Return the number of milliseconds contained in the current millennium + * @method int millisecondsInMinute() Return the number of milliseconds contained in the current minute + * @method int millisecondsInMonth() Return the number of milliseconds contained in the current month + * @method int millisecondsInQuarter() Return the number of milliseconds contained in the current quarter + * @method int millisecondsInSecond() Return the number of milliseconds contained in the current second + * @method int millisecondsInWeek() Return the number of milliseconds contained in the current week + * @method int millisecondsInYear() Return the number of milliseconds contained in the current year + * @method int|static minuteOfCentury(?int $minute = null) Return the value of the minute starting from the beginning of the current century when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfDay(?int $minute = null) Return the value of the minute starting from the beginning of the current day when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfDecade(?int $minute = null) Return the value of the minute starting from the beginning of the current decade when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfHour(?int $minute = null) Return the value of the minute starting from the beginning of the current hour when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfMillennium(?int $minute = null) Return the value of the minute starting from the beginning of the current millennium when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfMonth(?int $minute = null) Return the value of the minute starting from the beginning of the current month when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfQuarter(?int $minute = null) Return the value of the minute starting from the beginning of the current quarter when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfWeek(?int $minute = null) Return the value of the minute starting from the beginning of the current week when called with no parameters, change the current minute when called with an integer value + * @method int|static minuteOfYear(?int $minute = null) Return the value of the minute starting from the beginning of the current year when called with no parameters, change the current minute when called with an integer value + * @method int minutesInCentury() Return the number of minutes contained in the current century + * @method int minutesInDay() Return the number of minutes contained in the current day + * @method int minutesInDecade() Return the number of minutes contained in the current decade + * @method int minutesInHour() Return the number of minutes contained in the current hour + * @method int minutesInMillennium() Return the number of minutes contained in the current millennium + * @method int minutesInMonth() Return the number of minutes contained in the current month + * @method int minutesInQuarter() Return the number of minutes contained in the current quarter + * @method int minutesInWeek() Return the number of minutes contained in the current week + * @method int minutesInYear() Return the number of minutes contained in the current year + * @method int|static monthOfCentury(?int $month = null) Return the value of the month starting from the beginning of the current century when called with no parameters, change the current month when called with an integer value + * @method int|static monthOfDecade(?int $month = null) Return the value of the month starting from the beginning of the current decade when called with no parameters, change the current month when called with an integer value + * @method int|static monthOfMillennium(?int $month = null) Return the value of the month starting from the beginning of the current millennium when called with no parameters, change the current month when called with an integer value + * @method int|static monthOfQuarter(?int $month = null) Return the value of the month starting from the beginning of the current quarter when called with no parameters, change the current month when called with an integer value + * @method int|static monthOfYear(?int $month = null) Return the value of the month starting from the beginning of the current year when called with no parameters, change the current month when called with an integer value + * @method int monthsInCentury() Return the number of months contained in the current century + * @method int monthsInDecade() Return the number of months contained in the current decade + * @method int monthsInMillennium() Return the number of months contained in the current millennium + * @method int monthsInQuarter() Return the number of months contained in the current quarter + * @method int monthsInYear() Return the number of months contained in the current year + * @method int|static quarterOfCentury(?int $quarter = null) Return the value of the quarter starting from the beginning of the current century when called with no parameters, change the current quarter when called with an integer value + * @method int|static quarterOfDecade(?int $quarter = null) Return the value of the quarter starting from the beginning of the current decade when called with no parameters, change the current quarter when called with an integer value + * @method int|static quarterOfMillennium(?int $quarter = null) Return the value of the quarter starting from the beginning of the current millennium when called with no parameters, change the current quarter when called with an integer value + * @method int|static quarterOfYear(?int $quarter = null) Return the value of the quarter starting from the beginning of the current year when called with no parameters, change the current quarter when called with an integer value + * @method int quartersInCentury() Return the number of quarters contained in the current century + * @method int quartersInDecade() Return the number of quarters contained in the current decade + * @method int quartersInMillennium() Return the number of quarters contained in the current millennium + * @method int quartersInYear() Return the number of quarters contained in the current year + * @method int|static secondOfCentury(?int $second = null) Return the value of the second starting from the beginning of the current century when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfDay(?int $second = null) Return the value of the second starting from the beginning of the current day when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfDecade(?int $second = null) Return the value of the second starting from the beginning of the current decade when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfHour(?int $second = null) Return the value of the second starting from the beginning of the current hour when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfMillennium(?int $second = null) Return the value of the second starting from the beginning of the current millennium when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfMinute(?int $second = null) Return the value of the second starting from the beginning of the current minute when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfMonth(?int $second = null) Return the value of the second starting from the beginning of the current month when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfQuarter(?int $second = null) Return the value of the second starting from the beginning of the current quarter when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfWeek(?int $second = null) Return the value of the second starting from the beginning of the current week when called with no parameters, change the current second when called with an integer value + * @method int|static secondOfYear(?int $second = null) Return the value of the second starting from the beginning of the current year when called with no parameters, change the current second when called with an integer value + * @method int secondsInCentury() Return the number of seconds contained in the current century + * @method int secondsInDay() Return the number of seconds contained in the current day + * @method int secondsInDecade() Return the number of seconds contained in the current decade + * @method int secondsInHour() Return the number of seconds contained in the current hour + * @method int secondsInMillennium() Return the number of seconds contained in the current millennium + * @method int secondsInMinute() Return the number of seconds contained in the current minute + * @method int secondsInMonth() Return the number of seconds contained in the current month + * @method int secondsInQuarter() Return the number of seconds contained in the current quarter + * @method int secondsInWeek() Return the number of seconds contained in the current week + * @method int secondsInYear() Return the number of seconds contained in the current year + * @method int|static weekOfCentury(?int $week = null) Return the value of the week starting from the beginning of the current century when called with no parameters, change the current week when called with an integer value + * @method int|static weekOfDecade(?int $week = null) Return the value of the week starting from the beginning of the current decade when called with no parameters, change the current week when called with an integer value + * @method int|static weekOfMillennium(?int $week = null) Return the value of the week starting from the beginning of the current millennium when called with no parameters, change the current week when called with an integer value + * @method int|static weekOfMonth(?int $week = null) Return the value of the week starting from the beginning of the current month when called with no parameters, change the current week when called with an integer value + * @method int|static weekOfQuarter(?int $week = null) Return the value of the week starting from the beginning of the current quarter when called with no parameters, change the current week when called with an integer value + * @method int|static weekOfYear(?int $week = null) Return the value of the week starting from the beginning of the current year when called with no parameters, change the current week when called with an integer value + * @method int weeksInCentury() Return the number of weeks contained in the current century + * @method int weeksInDecade() Return the number of weeks contained in the current decade + * @method int weeksInMillennium() Return the number of weeks contained in the current millennium + * @method int weeksInMonth() Return the number of weeks contained in the current month + * @method int weeksInQuarter() Return the number of weeks contained in the current quarter + * @method int|static yearOfCentury(?int $year = null) Return the value of the year starting from the beginning of the current century when called with no parameters, change the current year when called with an integer value + * @method int|static yearOfDecade(?int $year = null) Return the value of the year starting from the beginning of the current decade when called with no parameters, change the current year when called with an integer value + * @method int|static yearOfMillennium(?int $year = null) Return the value of the year starting from the beginning of the current millennium when called with no parameters, change the current year when called with an integer value + * @method int yearsInCentury() Return the number of years contained in the current century + * @method int yearsInDecade() Return the number of years contained in the current decade + * @method int yearsInMillennium() Return the number of years contained in the current millennium * * */ @@ -566,13 +902,6 @@ trait Date CarbonInterface::SATURDAY => 'Saturday', ]; - /** - * Will UTF8 encoding be used to print localized date/time ? - * - * @var bool - */ - protected static $utf8 = false; - /** * List of unit and magic methods associated as doc-comments. * @@ -614,37 +943,32 @@ trait Date /** * Creates a DateTimeZone from a string, DateTimeZone or integer offset. * - * @param DateTimeZone|string|int|null $object original value to get CarbonTimeZone from it. - * @param DateTimeZone|string|int|null $objectDump dump of the object for error messages. + * @param DateTimeZone|string|int|false|null $object original value to get CarbonTimeZone from it. + * @param DateTimeZone|string|int|false|null $objectDump dump of the object for error messages. * * @throws InvalidTimeZoneException * - * @return CarbonTimeZone|false + * @return CarbonTimeZone|null */ - protected static function safeCreateDateTimeZone($object, $objectDump = null) - { + protected static function safeCreateDateTimeZone( + DateTimeZone|string|int|false|null $object, + DateTimeZone|string|int|false|null $objectDump = null, + ): ?CarbonTimeZone { return CarbonTimeZone::instance($object, $objectDump); } /** * Get the TimeZone associated with the Carbon instance (as CarbonTimeZone). * - * @return CarbonTimeZone - * * @link https://php.net/manual/en/datetime.gettimezone.php */ - #[ReturnTypeWillChange] - public function getTimezone() + public function getTimezone(): CarbonTimeZone { - return CarbonTimeZone::instance(parent::getTimezone()); + return $this->transmitFactory(fn () => CarbonTimeZone::instance(parent::getTimezone())); } /** * List of minimum and maximums for each unit. - * - * @param int $daysInMonth - * - * @return array */ protected static function getRangesByUnit(int $daysInMonth = 31): array { @@ -694,7 +1018,7 @@ public function clone() * * @return static */ - public function avoidMutation(): self + public function avoidMutation(): static { if ($this instanceof DateTimeImmutable) { return $this; @@ -708,79 +1032,11 @@ public function avoidMutation(): self * * @return static */ - public function nowWithSameTz() - { - return static::now($this->getTimezone()); - } - - /** - * Throws an exception if the given object is not a DateTime and does not implement DateTimeInterface. - * - * @param mixed $date - * @param string|array $other - * - * @throws InvalidTypeException - */ - protected static function expectDateTime($date, $other = []) - { - $message = 'Expected '; - foreach ((array) $other as $expect) { - $message .= "$expect, "; - } - - if (!$date instanceof DateTime && !$date instanceof DateTimeInterface) { - throw new InvalidTypeException( - $message.'DateTime or DateTimeInterface, '. - (\is_object($date) ? \get_class($date) : \gettype($date)).' given' - ); - } - } - - /** - * Return the Carbon instance passed through, a now instance in the same timezone - * if null given or parse the input if string given. - * - * @param Carbon|DateTimeInterface|string|null $date - * - * @return static - */ - protected function resolveCarbon($date = null) - { - if (!$date) { - return $this->nowWithSameTz(); - } - - if (\is_string($date)) { - return static::parse($date, $this->getTimezone()); - } - - static::expectDateTime($date, ['null', 'string']); - - return $date instanceof self ? $date : static::instance($date); - } - - /** - * Return the Carbon instance passed through, a now instance in UTC - * if null given or parse the input if string given (using current timezone - * then switching to UTC). - * - * @param Carbon|DateTimeInterface|string|null $date - * - * @return static - */ - protected function resolveUTC($date = null): self + public function nowWithSameTz(): static { - if (!$date) { - return static::now('UTC'); - } + $timezone = $this->getTimezone(); - if (\is_string($date)) { - return static::parse($date, $this->getTimezone())->utc(); - } - - static::expectDateTime($date, ['null', 'string']); - - return $date instanceof self ? $date : static::instance($date)->utc(); + return $this->getClock()?->nowAs(static::class, $timezone) ?? static::now($timezone); } /** @@ -809,37 +1065,51 @@ public function carbonize($date = null) /////////////////////////////////////////////////////////////////// /** - * Get a part of the Carbon object - * - * @param string $name + * Get a part of the Carbon object. * * @throws UnknownGetterException * * @return string|int|bool|DateTimeZone|null */ - public function __get($name) + public function __get(string $name): mixed { return $this->get($name); } /** - * Get a part of the Carbon object - * - * @param string $name + * Get a part of the Carbon object. * * @throws UnknownGetterException * - * @return string|int|bool|DateTimeZone|null + * @return string|int|bool|DateTimeZone */ - public function get($name) + public function get(Unit|string $name): mixed { + static $localizedFormats = [ + // @property string the day of week in current locale + 'localeDayOfWeek' => 'dddd', + // @property string the abbreviated day of week in current locale + 'shortLocaleDayOfWeek' => 'ddd', + // @property string the month in current locale + 'localeMonth' => 'MMMM', + // @property string the abbreviated month in current locale + 'shortLocaleMonth' => 'MMM', + ]; + + $name = Unit::toName($name); + + if (isset($localizedFormats[$name])) { + return $this->isoFormat($localizedFormats[$name]); + } + static $formats = [ // @property int 'year' => 'Y', // @property int 'yearIso' => 'o', + // @--property-read int + // @--property-write Month|int // @property int - // @call isSameUnit 'month' => 'n', // @property int 'day' => 'j', @@ -853,11 +1123,11 @@ public function get($name) 'micro' => 'u', // @property int 'microsecond' => 'u', - // @property-read int 0 (for Sunday) through 6 (for Saturday) + // @property int 0 (for Sunday) through 6 (for Saturday) 'dayOfWeek' => 'w', - // @property-read int 1 (for Monday) through 7 (for Sunday) + // @property int 1 (for Monday) through 7 (for Sunday) 'dayOfWeekIso' => 'N', - // @property-read int ISO-8601 week number of year, weeks starting on Monday + // @property int ISO-8601 week number of year, weeks starting on Monday 'weekOfYear' => 'W', // @property-read int number of days in the given month 'daysInMonth' => 't', @@ -875,30 +1145,6 @@ public function get($name) 'englishMonth' => 'F', // @property string the abbreviated month in English 'shortEnglishMonth' => 'M', - // @property string the day of week in current locale LC_TIME - // @deprecated - // reason: It uses OS language package and strftime() which is deprecated since PHP 8.1. - // replacement: Use ->isoFormat('MMM') instead. - // since: 2.55.0 - 'localeDayOfWeek' => '%A', - // @property string the abbreviated day of week in current locale LC_TIME - // @deprecated - // reason: It uses OS language package and strftime() which is deprecated since PHP 8.1. - // replacement: Use ->isoFormat('dddd') instead. - // since: 2.55.0 - 'shortLocaleDayOfWeek' => '%a', - // @property string the month in current locale LC_TIME - // @deprecated - // reason: It uses OS language package and strftime() which is deprecated since PHP 8.1. - // replacement: Use ->isoFormat('ddd') instead. - // since: 2.55.0 - 'localeMonth' => '%B', - // @property string the abbreviated month in current locale LC_TIME - // @deprecated - // reason: It uses OS language package and strftime() which is deprecated since PHP 8.1. - // replacement: Use ->isoFormat('MMMM') instead. - // since: 2.55.0 - 'shortLocaleMonth' => '%b', // @property-read string $timezoneAbbreviatedName the current timezone abbreviated name 'timezoneAbbreviatedName' => 'T', // @property-read string $tzAbbrName alias of $timezoneAbbreviatedName @@ -907,9 +1153,7 @@ public function get($name) switch (true) { case isset($formats[$name]): - $format = $formats[$name]; - $method = str_starts_with($format, '%') ? 'formatLocalized' : 'rawFormat'; - $value = $this->$method($format); + $value = $this->rawFormat($formats[$name]); return is_numeric($value) ? (int) $value : $value; @@ -969,7 +1213,7 @@ public function get($name) case $name === 'isoWeeksInYear': return $this->isoWeeksInYear(); - // @property-read int 1 through 5 + // @property int 1 through 5 case $name === 'weekOfMonth': return (int) ceil($this->day / static::DAYS_PER_WEEK); @@ -979,11 +1223,11 @@ public function get($name) // @property-read int 0 through 6 case $name === 'firstWeekDay': - return $this->localTranslator ? ($this->getTranslationMessage('first_day_of_week') ?? 0) : static::getWeekStartsAt(); + return (int) $this->getTranslationMessage('first_day_of_week'); // @property-read int 0 through 6 case $name === 'lastWeekDay': - return $this->localTranslator ? (($this->getTranslationMessage('first_day_of_week') ?? 0) + static::DAYS_PER_WEEK - 1) % static::DAYS_PER_WEEK : static::getWeekEndsAt(); + return $this->transmitFactory(fn () => static::weekRotate((int) $this->getTranslationMessage('first_day_of_week'), -1)); // @property int 1 through 366 case $name === 'dayOfYear': @@ -991,14 +1235,13 @@ public function get($name) // @property-read int 365 or 366 case $name === 'daysInYear': - return $this->isLeapYear() ? 366 : 365; + return static::DAYS_PER_YEAR + ($this->isLeapYear() ? 1 : 0); // @property int does a diffInYears() with default parameters case $name === 'age': - return $this->diffInYears(); + return (int) $this->diffInYears(); // @property-read int the quarter of this instance, 1 - 4 - // @call isSameUnit case $name === 'quarter': return (int) ceil($this->month / static::MONTHS_PER_QUARTER); @@ -1012,6 +1255,7 @@ public function get($name) case $name === 'century': $factor = 1; $year = $this->year; + if ($year < 0) { $year = -$year; $factor = -1; @@ -1024,6 +1268,7 @@ public function get($name) case $name === 'millennium': $factor = 1; $year = $this->year; + if ($year < 0) { $year = -$year; $factor = -1; @@ -1055,10 +1300,14 @@ public function get($name) case $name === 'utc': return $this->getOffset() === 0; + // @--property-write DateTimeZone|string|int $timezone the current timezone + // @--property-write DateTimeZone|string|int $tz alias of $timezone + // @--property-read CarbonTimeZone $timezone the current timezone + // @--property-read CarbonTimeZone $tz alias of $timezone // @property CarbonTimeZone $timezone the current timezone // @property CarbonTimeZone $tz alias of $timezone case $name === 'timezone' || $name === 'tz': - return CarbonTimeZone::instance($this->getTimezone()); + return $this->getTimezone(); // @property-read string $timezoneName the current timezone name // @property-read string $tzName alias of $timezoneName @@ -1069,6 +1318,26 @@ public function get($name) case $name === 'locale': return $this->getTranslatorLocale(); + case preg_match('/^([a-z]{2,})(In|Of)([A-Z][a-z]+)$/', $name, $match): + [, $firstUnit, $operator, $secondUnit] = $match; + + try { + $start = $this->avoidMutation()->startOf($secondUnit); + $value = $operator === 'Of' + ? (\in_array($firstUnit, [ + // Unit with indexes starting at 1 (other units start at 0) + 'day', + 'week', + 'month', + 'quarter', + ], true) ? 1 : 0) + floor($start->diffInUnit($firstUnit, $this)) + : round($start->diffInUnit($firstUnit, $start->avoidMutation()->add($secondUnit, 1))); + + return (int) $value; + } catch (UnknownUnitException) { + // default to macro + } + default: $macro = $this->getLocalMacro('get'.ucfirst($name)); @@ -1091,7 +1360,7 @@ public function __isset($name) { try { $this->__get($name); - } catch (UnknownGetterException | ReflectionException $e) { + } catch (UnknownGetterException | ReflectionException) { return false; } @@ -1120,19 +1389,16 @@ public function __set($name, $value) } /** - * Set a part of the Carbon object - * - * @param string|array $name - * @param string|int|DateTimeZone $value + * Set a part of the Carbon object. * * @throws ImmutableException|UnknownSetterException * * @return $this */ - public function set($name, $value = null) + public function set(Unit|array|string $name, DateTimeZone|Month|string|int|float|null $value = null): static { if ($this->isImmutable()) { - throw new ImmutableException(sprintf('%s class', static::class)); + throw new ImmutableException(\sprintf('%s class', static::class)); } if (\is_array($name)) { @@ -1143,6 +1409,8 @@ public function set($name, $value = null) return $this; } + $name = Unit::toName($name); + switch ($name) { case 'milliseconds': case 'millisecond': @@ -1175,7 +1443,7 @@ public function set($name, $value = null) case 'minute': case 'second': [$year, $month, $day, $hour, $minute, $second] = array_map('intval', explode('-', $this->rawFormat('Y-n-j-G-i-s'))); - $$name = $value; + $$name = self::monthToInt($value, $name); $this->setDateTime($year, $month, $day, $hour, $minute, $second); break; @@ -1205,6 +1473,16 @@ public function set($name, $value = null) break; + case 'dayOfWeek': + $this->addDays($value - $this->dayOfWeek); + + break; + + case 'dayOfWeekIso': + $this->addDays($value - $this->dayOfWeekIso); + + break; + case 'timestamp': $this->setTimestamp($value); @@ -1232,6 +1510,32 @@ public function set($name, $value = null) break; default: + if (preg_match('/^([a-z]{2,})Of([A-Z][a-z]+)$/', $name, $match)) { + [, $firstUnit, $secondUnit] = $match; + + try { + $start = $this->avoidMutation()->startOf($secondUnit); + $currentValue = (\in_array($firstUnit, [ + // Unit with indexes starting at 1 (other units start at 0) + 'day', + 'week', + 'month', + 'quarter', + ], true) ? 1 : 0) + (int) floor($start->diffInUnit($firstUnit, $this)); + + // We check $value a posteriori to give precedence to UnknownUnitException + if (!\is_int($value)) { + throw new UnitException("->$name expects integer value"); + } + + $this->addUnit($firstUnit, $value - $currentValue); + + break; + } catch (UnknownUnitException) { + // default to macro + } + } + $macro = $this->getLocalMacro('set'.ucfirst($name)); if ($macro) { @@ -1240,7 +1544,7 @@ public function set($name, $value = null) break; } - if ($this->localStrictModeEnabled ?? static::isStrictModeEnabled()) { + if ($this->isLocalStrictModeEnabled()) { throw new UnknownSetterException($name); } @@ -1250,37 +1554,18 @@ public function set($name, $value = null) return $this; } - protected function getTranslatedFormByRegExp($baseKey, $keySuffix, $context, $subKey, $defaultValue) - { - $key = $baseKey.$keySuffix; - $standaloneKey = "{$key}_standalone"; - $baseTranslation = $this->getTranslationMessage($key); - - if ($baseTranslation instanceof Closure) { - return $baseTranslation($this, $context, $subKey) ?: $defaultValue; - } - - if ( - $this->getTranslationMessage("$standaloneKey.$subKey") && - (!$context || (($regExp = $this->getTranslationMessage("{$baseKey}_regexp")) && !preg_match($regExp, $context))) - ) { - $key = $standaloneKey; - } - - return $this->getTranslationMessage("$key.$subKey", null, $defaultValue); - } - /** * Get the translation of the current week day name (with context for languages with multiple forms). * * @param string|null $context whole format string * @param string $keySuffix "", "_short" or "_min" * @param string|null $defaultValue default value if translation missing - * - * @return string */ - public function getTranslatedDayName($context = null, $keySuffix = '', $defaultValue = null) - { + public function getTranslatedDayName( + ?string $context = null, + string $keySuffix = '', + ?string $defaultValue = null, + ): string { return $this->getTranslatedFormByRegExp('weekdays', $keySuffix, $context, $this->dayOfWeek, $defaultValue ?: $this->englishDayOfWeek); } @@ -1288,10 +1573,8 @@ public function getTranslatedDayName($context = null, $keySuffix = '', $defaultV * Get the translation of the current short week day name (with context for languages with multiple forms). * * @param string|null $context whole format string - * - * @return string */ - public function getTranslatedShortDayName($context = null) + public function getTranslatedShortDayName(?string $context = null): string { return $this->getTranslatedDayName($context, '_short', $this->shortEnglishDayOfWeek); } @@ -1300,10 +1583,8 @@ public function getTranslatedShortDayName($context = null) * Get the translation of the current abbreviated week day name (with context for languages with multiple forms). * * @param string|null $context whole format string - * - * @return string */ - public function getTranslatedMinDayName($context = null) + public function getTranslatedMinDayName(?string $context = null): string { return $this->getTranslatedDayName($context, '_min', $this->shortEnglishDayOfWeek); } @@ -1314,11 +1595,12 @@ public function getTranslatedMinDayName($context = null) * @param string|null $context whole format string * @param string $keySuffix "" or "_short" * @param string|null $defaultValue default value if translation missing - * - * @return string */ - public function getTranslatedMonthName($context = null, $keySuffix = '', $defaultValue = null) - { + public function getTranslatedMonthName( + ?string $context = null, + string $keySuffix = '', + ?string $defaultValue = null, + ): string { return $this->getTranslatedFormByRegExp('months', $keySuffix, $context, $this->month - 1, $defaultValue ?: $this->englishMonth); } @@ -1326,10 +1608,8 @@ public function getTranslatedMonthName($context = null, $keySuffix = '', $defaul * Get the translation of the current short month day name (with context for languages with multiple forms). * * @param string|null $context whole format string - * - * @return string */ - public function getTranslatedShortMonthName($context = null) + public function getTranslatedShortMonthName(?string $context = null): string { return $this->getTranslatedMonthName($context, '_short', $this->shortEnglishMonth); } @@ -1337,11 +1617,17 @@ public function getTranslatedShortMonthName($context = null) /** * Get/set the day of year. * + * @template T of int|null + * * @param int|null $value new value for day of year if using as setter. * + * @psalm-param T $value + * * @return static|int + * + * @psalm-return (T is int ? static : int) */ - public function dayOfYear($value = null) + public function dayOfYear(?int $value = null): static|int { $dayOfYear = $this->dayOfYear; @@ -1351,11 +1637,9 @@ public function dayOfYear($value = null) /** * Get/set the weekday from 0 (Sunday) to 6 (Saturday). * - * @param int|null $value new value for weekday if using as setter. - * - * @return static|int + * @param WeekDay|int|null $value new value for weekday if using as setter. */ - public function weekday($value = null) + public function weekday(WeekDay|int|null $value = null): static|int { if ($value === null) { return $this->dayOfWeek; @@ -1364,36 +1648,32 @@ public function weekday($value = null) $firstDay = (int) ($this->getTranslationMessage('first_day_of_week') ?? 0); $dayOfWeek = ($this->dayOfWeek + 7 - $firstDay) % 7; - return $this->addDays((($value + 7 - $firstDay) % 7) - $dayOfWeek); + return $this->addDays(((WeekDay::int($value) + 7 - $firstDay) % 7) - $dayOfWeek); } /** * Get/set the ISO weekday from 1 (Monday) to 7 (Sunday). * - * @param int|null $value new value for weekday if using as setter. - * - * @return static|int + * @param WeekDay|int|null $value new value for weekday if using as setter. */ - public function isoWeekday($value = null) + public function isoWeekday(WeekDay|int|null $value = null): static|int { $dayOfWeekIso = $this->dayOfWeekIso; - return $value === null ? $dayOfWeekIso : $this->addDays($value - $dayOfWeekIso); + return $value === null ? $dayOfWeekIso : $this->addDays(WeekDay::int($value) - $dayOfWeekIso); } /** * Return the number of days since the start of the week (using the current locale or the first parameter * if explicitly given). * - * @param int|null $weekStartsAt optional start allow you to specify the day of week to use to start the week, - * if not provided, start of week is inferred from the locale - * (Sunday for en_US, Monday for de_DE, etc.) - * - * @return int + * @param WeekDay|int|null $weekStartsAt optional start allow you to specify the day of week to use to start the week, + * if not provided, start of week is inferred from the locale + * (Sunday for en_US, Monday for de_DE, etc.) */ - public function getDaysFromStartOfWeek(int $weekStartsAt = null): int + public function getDaysFromStartOfWeek(WeekDay|int|null $weekStartsAt = null): int { - $firstDay = (int) ($weekStartsAt ?? $this->getTranslationMessage('first_day_of_week') ?? 0); + $firstDay = (int) (WeekDay::int($weekStartsAt) ?? $this->getTranslationMessage('first_day_of_week') ?? 0); return ($this->dayOfWeek + 7 - $firstDay) % 7; } @@ -1402,16 +1682,14 @@ public function getDaysFromStartOfWeek(int $weekStartsAt = null): int * Set the day (keeping the current time) to the start of the week + the number of days passed as the first * parameter. First day of week is driven by the locale unless explicitly set with the second parameter. * - * @param int $numberOfDays number of days to add after the start of the current week - * @param int|null $weekStartsAt optional start allow you to specify the day of week to use to start the week, - * if not provided, start of week is inferred from the locale - * (Sunday for en_US, Monday for de_DE, etc.) - * - * @return static + * @param int $numberOfDays number of days to add after the start of the current week + * @param WeekDay|int|null $weekStartsAt optional start allow you to specify the day of week to use to start the week, + * if not provided, start of week is inferred from the locale + * (Sunday for en_US, Monday for de_DE, etc.) */ - public function setDaysFromStartOfWeek(int $numberOfDays, int $weekStartsAt = null) + public function setDaysFromStartOfWeek(int $numberOfDays, WeekDay|int|null $weekStartsAt = null): static { - return $this->addDays($numberOfDays - $this->getDaysFromStartOfWeek($weekStartsAt)); + return $this->addDays($numberOfDays - $this->getDaysFromStartOfWeek(WeekDay::int($weekStartsAt))); } /** @@ -1420,21 +1698,21 @@ public function setDaysFromStartOfWeek(int $numberOfDays, int $weekStartsAt = nu * @param string $valueUnit unit name to modify * @param int $value new value for the input unit * @param string $overflowUnit unit name to not overflow - * - * @return static */ - public function setUnitNoOverflow($valueUnit, $value, $overflowUnit) + public function setUnitNoOverflow(string $valueUnit, int $value, string $overflowUnit): static { try { - $original = $this->avoidMutation(); + $start = $this->avoidMutation()->startOf($overflowUnit); + $end = $this->avoidMutation()->endOf($overflowUnit); /** @var static $date */ $date = $this->$valueUnit($value); - $end = $original->avoidMutation()->endOf($overflowUnit); - $start = $original->avoidMutation()->startOf($overflowUnit); + if ($date < $start) { - $date = $date->setDateTimeFrom($start); - } elseif ($date > $end) { - $date = $date->setDateTimeFrom($end); + return $date->mutateIfMutable($start); + } + + if ($date > $end) { + return $date->mutateIfMutable($end); } return $date; @@ -1449,10 +1727,8 @@ public function setUnitNoOverflow($valueUnit, $value, $overflowUnit) * @param string $valueUnit unit name to modify * @param int $value amount to add to the input unit * @param string $overflowUnit unit name to not overflow - * - * @return static */ - public function addUnitNoOverflow($valueUnit, $value, $overflowUnit) + public function addUnitNoOverflow(string $valueUnit, int $value, string $overflowUnit): static { return $this->setUnitNoOverflow($valueUnit, $this->$valueUnit + $value, $overflowUnit); } @@ -1463,24 +1739,18 @@ public function addUnitNoOverflow($valueUnit, $value, $overflowUnit) * @param string $valueUnit unit name to modify * @param int $value amount to subtract to the input unit * @param string $overflowUnit unit name to not overflow - * - * @return static */ - public function subUnitNoOverflow($valueUnit, $value, $overflowUnit) + public function subUnitNoOverflow(string $valueUnit, int $value, string $overflowUnit): static { return $this->setUnitNoOverflow($valueUnit, $this->$valueUnit - $value, $overflowUnit); } /** * Returns the minutes offset to UTC if no arguments passed, else set the timezone with given minutes shift passed. - * - * @param int|null $minuteOffset - * - * @return int|static */ - public function utcOffset(int $minuteOffset = null) + public function utcOffset(?int $minuteOffset = null): static|int { - if (\func_num_args() < 1) { + if ($minuteOffset === null) { return $this->offsetMinutes; } @@ -1491,97 +1761,63 @@ public function utcOffset(int $minuteOffset = null) * Set the date with gregorian year, month and day numbers. * * @see https://php.net/manual/en/datetime.setdate.php - * - * @param int $year - * @param int $month - * @param int $day - * - * @return static */ - #[ReturnTypeWillChange] - public function setDate($year, $month, $day) + public function setDate(int $year, int $month, int $day): static { - return parent::setDate((int) $year, (int) $month, (int) $day); + return parent::setDate($year, $month, $day); } /** * Set a date according to the ISO 8601 standard - using weeks and day offsets rather than specific dates. * * @see https://php.net/manual/en/datetime.setisodate.php - * - * @param int $year - * @param int $week - * @param int $day - * - * @return static */ - #[ReturnTypeWillChange] - public function setISODate($year, $week, $day = 1) + public function setISODate(int $year, int $week, int $day = 1): static { - return parent::setISODate((int) $year, (int) $week, (int) $day); + return parent::setISODate($year, $week, $day); } /** * Set the date and time all together. - * - * @param int $year - * @param int $month - * @param int $day - * @param int $hour - * @param int $minute - * @param int $second - * @param int $microseconds - * - * @return static */ - public function setDateTime($year, $month, $day, $hour, $minute, $second = 0, $microseconds = 0) - { - return $this->setDate($year, $month, $day)->setTime((int) $hour, (int) $minute, (int) $second, (int) $microseconds); + public function setDateTime( + int $year, + int $month, + int $day, + int $hour, + int $minute, + int $second = 0, + int $microseconds = 0, + ): static { + return $this->setDate($year, $month, $day)->setTime($hour, $minute, $second, $microseconds); } /** * Resets the current time of the DateTime object to a different time. * * @see https://php.net/manual/en/datetime.settime.php - * - * @param int $hour - * @param int $minute - * @param int $second - * @param int $microseconds - * - * @return static */ - #[ReturnTypeWillChange] - public function setTime($hour, $minute, $second = 0, $microseconds = 0) + public function setTime(int $hour, int $minute, int $second = 0, int $microseconds = 0): static { - return parent::setTime((int) $hour, (int) $minute, (int) $second, (int) $microseconds); + return parent::setTime($hour, $minute, $second, $microseconds); } /** * Set the instance's timestamp. * * Timestamp input can be given as int, float or a string containing one or more numbers. - * - * @param float|int|string $unixTimestamp - * - * @return static */ - #[ReturnTypeWillChange] - public function setTimestamp($unixTimestamp) + public function setTimestamp(float|int|string $timestamp): static { - [$timestamp, $microseconds] = self::getIntegerAndDecimalParts($unixTimestamp); + [$seconds, $microseconds] = self::getIntegerAndDecimalParts($timestamp); - return parent::setTimestamp((int) $timestamp)->setMicroseconds((int) $microseconds); + return parent::setTimestamp((int) $seconds)->setMicroseconds((int) $microseconds); } /** * Set the time by time string. - * - * @param string $time - * - * @return static */ - public function setTimeFromTimeString($time) + public function setTimeFromTimeString(string $time): static { if (!str_contains($time, ':')) { $time .= ':0'; @@ -1592,12 +1828,8 @@ public function setTimeFromTimeString($time) /** * @alias setTimezone - * - * @param DateTimeZone|string $value - * - * @return static */ - public function timezone($value) + public function timezone(DateTimeZone|string|int $value): static { return $this->setTimezone($value); } @@ -1605,13 +1837,11 @@ public function timezone($value) /** * Set the timezone or returns the timezone name if no arguments passed. * - * @param DateTimeZone|string $value - * - * @return static|string + * @return ($value is null ? string : static) */ - public function tz($value = null) + public function tz(DateTimeZone|string|int|null $value = null): static|string { - if (\func_num_args() < 1) { + if ($value === null) { return $this->tzName; } @@ -1620,31 +1850,16 @@ public function tz($value = null) /** * Set the instance's timezone from a string or object. - * - * @param DateTimeZone|string $value - * - * @return static */ - #[ReturnTypeWillChange] - public function setTimezone($value) + public function setTimezone(DateTimeZone|string|int $timeZone): static { - $tz = static::safeCreateDateTimeZone($value); - - if ($tz === false && !self::isStrictModeEnabled()) { - $tz = new CarbonTimeZone(); - } - - return parent::setTimezone($tz); + return parent::setTimezone(static::safeCreateDateTimeZone($timeZone)); } /** * Set the instance's timezone from a string or object and add/subtract the offset difference. - * - * @param DateTimeZone|string $value - * - * @return static */ - public function shiftTimezone($value) + public function shiftTimezone(DateTimeZone|string $value): static { $dateTimeString = $this->format('Y-m-d H:i:s.u'); @@ -1655,22 +1870,16 @@ public function shiftTimezone($value) /** * Set the instance's timezone to UTC. - * - * @return static */ - public function utc() + public function utc(): static { return $this->setTimezone('UTC'); } /** * Set the year, month, and date for this instance to that of the passed instance. - * - * @param Carbon|DateTimeInterface $date now if null - * - * @return static */ - public function setDateFrom($date = null) + public function setDateFrom(DateTimeInterface|string $date): static { $date = $this->resolveCarbon($date); @@ -1679,12 +1888,8 @@ public function setDateFrom($date = null) /** * Set the hour, minute, second and microseconds for this instance to that of the passed instance. - * - * @param Carbon|DateTimeInterface $date now if null - * - * @return static */ - public function setTimeFrom($date = null) + public function setTimeFrom(DateTimeInterface|string $date): static { $date = $this->resolveCarbon($date); @@ -1693,12 +1898,8 @@ public function setTimeFrom($date = null) /** * Set the date and time for this instance to that of the passed instance. - * - * @param Carbon|DateTimeInterface $date - * - * @return static */ - public function setDateTimeFrom($date = null) + public function setDateTimeFrom(DateTimeInterface|string $date): static { $date = $this->resolveCarbon($date); @@ -1706,11 +1907,9 @@ public function setDateTimeFrom($date = null) } /** - * Get the days of the week - * - * @return array + * Get the days of the week. */ - public static function getDays() + public static function getDays(): array { return static::$days; } @@ -1719,85 +1918,37 @@ public static function getDays() /////////////////////// WEEK SPECIAL DAYS ///////////////////////// /////////////////////////////////////////////////////////////////// - private static function getFirstDayOfWeek(): int - { - return (int) static::getTranslationMessageWith( - static::getTranslator(), - 'first_day_of_week' - ); - } - /** - * Get the first day of week + * Get the first day of week. * * @return int */ - public static function getWeekStartsAt() + public static function getWeekStartsAt(?string $locale = null): int { - if (static::$weekStartsAt === static::WEEK_DAY_AUTO) { - return self::getFirstDayOfWeek(); - } - - return static::$weekStartsAt; + return (int) static::getTranslationMessageWith( + $locale ? Translator::get($locale) : static::getTranslator(), + 'first_day_of_week', + ); } /** - * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * Use $weekEndsAt optional parameter instead when using endOfWeek method. You can also use the - * 'first_day_of_week' locale setting to change the start of week according to current locale - * selected and implicitly the end of week. - * - * Set the first day of week + * Get the last day of week. * - * @param int|string $day week start day (or 'auto' to get the first day of week from Carbon::getLocale() culture). - * - * @return void - */ - public static function setWeekStartsAt($day) - { - static::$weekStartsAt = $day === static::WEEK_DAY_AUTO ? $day : max(0, (7 + $day) % 7); - } - - /** - * Get the last day of week + * @param string $locale local to consider the last day of week. * * @return int */ - public static function getWeekEndsAt() + public static function getWeekEndsAt(?string $locale = null): int { - if (static::$weekStartsAt === static::WEEK_DAY_AUTO) { - return (int) (static::DAYS_PER_WEEK - 1 + self::getFirstDayOfWeek()) % static::DAYS_PER_WEEK; - } - - return static::$weekEndsAt; - } - - /** - * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * Use $weekStartsAt optional parameter instead when using startOfWeek, floorWeek, ceilWeek - * or roundWeek method. You can also use the 'first_day_of_week' locale setting to change the - * start of week according to current locale selected and implicitly the end of week. - * - * Set the last day of week - * - * @param int|string $day week end day (or 'auto' to get the day before the first day of week - * from Carbon::getLocale() culture). - * - * @return void - */ - public static function setWeekEndsAt($day) - { - static::$weekEndsAt = $day === static::WEEK_DAY_AUTO ? $day : max(0, (7 + $day) % 7); + return static::weekRotate(static::getWeekStartsAt($locale), -1); } /** * Get weekend days - * - * @return array */ - public static function getWeekendDays() + public static function getWeekendDays(): array { - return static::$weekendDays; + return FactoryImmutable::getInstance()->getWeekendDays(); } /** @@ -1822,24 +1973,18 @@ public static function getWeekendDays() * ``` * * Set weekend days - * - * @param array $days - * - * @return void */ - public static function setWeekendDays($days) + public static function setWeekendDays(array $days): void { - static::$weekendDays = $days; + FactoryImmutable::getDefaultInstance()->setWeekendDays($days); } /** * Determine if a time string will produce a relative date. * - * @param string $time - * * @return bool true if time match a relative date, false if absolute or invalid time string */ - public static function hasRelativeKeywords($time) + public static function hasRelativeKeywords(?string $time): bool { if (!$time || strtotime($time) === false) { return false; @@ -1857,68 +2002,20 @@ public static function hasRelativeKeywords($time) /////////////////////// STRING FORMATTING ///////////////////////// /////////////////////////////////////////////////////////////////// - /** - * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use UTF-8 language packages on every machine. - * - * Set if UTF8 will be used for localized date/time. - * - * @param bool $utf8 - */ - public static function setUtf8($utf8) - { - static::$utf8 = $utf8; - } - - /** - * Format the instance with the current locale. You can set the current - * locale using setlocale() https://php.net/setlocale. - * - * @deprecated It uses OS language package and strftime() which is deprecated since PHP 8.1. - * Use ->isoFormat() instead. - * Deprecated since 2.55.0 - * - * @param string $format - * - * @return string - */ - public function formatLocalized($format) - { - // Check for Windows to find and replace the %e modifier correctly. - if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { - $format = preg_replace('#(?toDateTimeString()); - $formatted = ($this->localStrictModeEnabled ?? static::isStrictModeEnabled()) - ? strftime($format, $time) - : @strftime($format, $time); - - return static::$utf8 - ? ( - \function_exists('mb_convert_encoding') - ? mb_convert_encoding($formatted, 'UTF-8', mb_list_encodings()) - : utf8_encode($formatted) - ) - : $formatted; - } - /** * Returns list of locale formats for ISO formatting. * * @param string|null $locale current locale used if null - * - * @return array */ - public function getIsoFormats($locale = null) + public function getIsoFormats(?string $locale = null): array { return [ - 'LT' => $this->getTranslationMessage('formats.LT', $locale, 'h:mm A'), - 'LTS' => $this->getTranslationMessage('formats.LTS', $locale, 'h:mm:ss A'), - 'L' => $this->getTranslationMessage('formats.L', $locale, 'MM/DD/YYYY'), - 'LL' => $this->getTranslationMessage('formats.LL', $locale, 'MMMM D, YYYY'), - 'LLL' => $this->getTranslationMessage('formats.LLL', $locale, 'MMMM D, YYYY h:mm A'), - 'LLLL' => $this->getTranslationMessage('formats.LLLL', $locale, 'dddd, MMMM D, YYYY h:mm A'), + 'LT' => $this->getTranslationMessage('formats.LT', $locale), + 'LTS' => $this->getTranslationMessage('formats.LTS', $locale), + 'L' => $this->getTranslationMessage('formats.L', $locale), + 'LL' => $this->getTranslationMessage('formats.LL', $locale), + 'LLL' => $this->getTranslationMessage('formats.LLL', $locale), + 'LLLL' => $this->getTranslationMessage('formats.LLLL', $locale), 'l' => $this->getTranslationMessage('formats.l', $locale), 'll' => $this->getTranslationMessage('formats.ll', $locale), 'lll' => $this->getTranslationMessage('formats.lll', $locale), @@ -1930,10 +2027,8 @@ public function getIsoFormats($locale = null) * Returns list of calendar formats for ISO formatting. * * @param string|null $locale current locale used if null - * - * @return array */ - public function getCalendarFormats($locale = null) + public function getCalendarFormats(?string $locale = null): array { return [ 'sameDay' => $this->getTranslationMessage('calendar.sameDay', $locale, '[Today at] LT'), @@ -1947,129 +2042,108 @@ public function getCalendarFormats($locale = null) /** * Returns list of locale units for ISO formatting. - * - * @return array */ - public static function getIsoUnits() + public static function getIsoUnits(): array { static $units = null; - if ($units === null) { - $units = [ - 'OD' => ['getAltNumber', ['day']], - 'OM' => ['getAltNumber', ['month']], - 'OY' => ['getAltNumber', ['year']], - 'OH' => ['getAltNumber', ['hour']], - 'Oh' => ['getAltNumber', ['h']], - 'Om' => ['getAltNumber', ['minute']], - 'Os' => ['getAltNumber', ['second']], - 'D' => 'day', - 'DD' => ['rawFormat', ['d']], - 'Do' => ['ordinal', ['day', 'D']], - 'd' => 'dayOfWeek', - 'dd' => function (CarbonInterface $date, $originalFormat = null) { - return $date->getTranslatedMinDayName($originalFormat); - }, - 'ddd' => function (CarbonInterface $date, $originalFormat = null) { - return $date->getTranslatedShortDayName($originalFormat); - }, - 'dddd' => function (CarbonInterface $date, $originalFormat = null) { - return $date->getTranslatedDayName($originalFormat); - }, - 'DDD' => 'dayOfYear', - 'DDDD' => ['getPaddedUnit', ['dayOfYear', 3]], - 'DDDo' => ['ordinal', ['dayOfYear', 'DDD']], - 'e' => ['weekday', []], - 'E' => 'dayOfWeekIso', - 'H' => ['rawFormat', ['G']], - 'HH' => ['rawFormat', ['H']], - 'h' => ['rawFormat', ['g']], - 'hh' => ['rawFormat', ['h']], - 'k' => 'noZeroHour', - 'kk' => ['getPaddedUnit', ['noZeroHour']], - 'hmm' => ['rawFormat', ['gi']], - 'hmmss' => ['rawFormat', ['gis']], - 'Hmm' => ['rawFormat', ['Gi']], - 'Hmmss' => ['rawFormat', ['Gis']], - 'm' => 'minute', - 'mm' => ['rawFormat', ['i']], - 'a' => 'meridiem', - 'A' => 'upperMeridiem', - 's' => 'second', - 'ss' => ['getPaddedUnit', ['second']], - 'S' => function (CarbonInterface $date) { - return (string) floor($date->micro / 100000); - }, - 'SS' => function (CarbonInterface $date) { - return str_pad((string) floor($date->micro / 10000), 2, '0', STR_PAD_LEFT); - }, - 'SSS' => function (CarbonInterface $date) { - return str_pad((string) floor($date->micro / 1000), 3, '0', STR_PAD_LEFT); - }, - 'SSSS' => function (CarbonInterface $date) { - return str_pad((string) floor($date->micro / 100), 4, '0', STR_PAD_LEFT); - }, - 'SSSSS' => function (CarbonInterface $date) { - return str_pad((string) floor($date->micro / 10), 5, '0', STR_PAD_LEFT); - }, - 'SSSSSS' => ['getPaddedUnit', ['micro', 6]], - 'SSSSSSS' => function (CarbonInterface $date) { - return str_pad((string) floor($date->micro * 10), 7, '0', STR_PAD_LEFT); - }, - 'SSSSSSSS' => function (CarbonInterface $date) { - return str_pad((string) floor($date->micro * 100), 8, '0', STR_PAD_LEFT); - }, - 'SSSSSSSSS' => function (CarbonInterface $date) { - return str_pad((string) floor($date->micro * 1000), 9, '0', STR_PAD_LEFT); - }, - 'M' => 'month', - 'MM' => ['rawFormat', ['m']], - 'MMM' => function (CarbonInterface $date, $originalFormat = null) { - $month = $date->getTranslatedShortMonthName($originalFormat); - $suffix = $date->getTranslationMessage('mmm_suffix'); - if ($suffix && $month !== $date->monthName) { - $month .= $suffix; - } + $units ??= [ + 'OD' => ['getAltNumber', ['day']], + 'OM' => ['getAltNumber', ['month']], + 'OY' => ['getAltNumber', ['year']], + 'OH' => ['getAltNumber', ['hour']], + 'Oh' => ['getAltNumber', ['h']], + 'Om' => ['getAltNumber', ['minute']], + 'Os' => ['getAltNumber', ['second']], + 'D' => 'day', + 'DD' => ['rawFormat', ['d']], + 'Do' => ['ordinal', ['day', 'D']], + 'd' => 'dayOfWeek', + 'dd' => static fn (CarbonInterface $date, $originalFormat = null) => $date->getTranslatedMinDayName( + $originalFormat, + ), + 'ddd' => static fn (CarbonInterface $date, $originalFormat = null) => $date->getTranslatedShortDayName( + $originalFormat, + ), + 'dddd' => static fn (CarbonInterface $date, $originalFormat = null) => $date->getTranslatedDayName( + $originalFormat, + ), + 'DDD' => 'dayOfYear', + 'DDDD' => ['getPaddedUnit', ['dayOfYear', 3]], + 'DDDo' => ['ordinal', ['dayOfYear', 'DDD']], + 'e' => ['weekday', []], + 'E' => 'dayOfWeekIso', + 'H' => ['rawFormat', ['G']], + 'HH' => ['rawFormat', ['H']], + 'h' => ['rawFormat', ['g']], + 'hh' => ['rawFormat', ['h']], + 'k' => 'noZeroHour', + 'kk' => ['getPaddedUnit', ['noZeroHour']], + 'hmm' => ['rawFormat', ['gi']], + 'hmmss' => ['rawFormat', ['gis']], + 'Hmm' => ['rawFormat', ['Gi']], + 'Hmmss' => ['rawFormat', ['Gis']], + 'm' => 'minute', + 'mm' => ['rawFormat', ['i']], + 'a' => 'meridiem', + 'A' => 'upperMeridiem', + 's' => 'second', + 'ss' => ['getPaddedUnit', ['second']], + 'S' => static fn (CarbonInterface $date) => (string) floor($date->micro / 100000), + 'SS' => static fn (CarbonInterface $date) => self::floorZeroPad($date->micro / 10000, 2), + 'SSS' => static fn (CarbonInterface $date) => self::floorZeroPad($date->micro / 1000, 3), + 'SSSS' => static fn (CarbonInterface $date) => self::floorZeroPad($date->micro / 100, 4), + 'SSSSS' => static fn (CarbonInterface $date) => self::floorZeroPad($date->micro / 10, 5), + 'SSSSSS' => ['getPaddedUnit', ['micro', 6]], + 'SSSSSSS' => static fn (CarbonInterface $date) => self::floorZeroPad($date->micro * 10, 7), + 'SSSSSSSS' => static fn (CarbonInterface $date) => self::floorZeroPad($date->micro * 100, 8), + 'SSSSSSSSS' => static fn (CarbonInterface $date) => self::floorZeroPad($date->micro * 1000, 9), + 'M' => 'month', + 'MM' => ['rawFormat', ['m']], + 'MMM' => static function (CarbonInterface $date, $originalFormat = null) { + $month = $date->getTranslatedShortMonthName($originalFormat); + $suffix = $date->getTranslationMessage('mmm_suffix'); + if ($suffix && $month !== $date->monthName) { + $month .= $suffix; + } - return $month; - }, - 'MMMM' => function (CarbonInterface $date, $originalFormat = null) { - return $date->getTranslatedMonthName($originalFormat); - }, - 'Mo' => ['ordinal', ['month', 'M']], - 'Q' => 'quarter', - 'Qo' => ['ordinal', ['quarter', 'M']], - 'G' => 'isoWeekYear', - 'GG' => ['getPaddedUnit', ['isoWeekYear']], - 'GGG' => ['getPaddedUnit', ['isoWeekYear', 3]], - 'GGGG' => ['getPaddedUnit', ['isoWeekYear', 4]], - 'GGGGG' => ['getPaddedUnit', ['isoWeekYear', 5]], - 'g' => 'weekYear', - 'gg' => ['getPaddedUnit', ['weekYear']], - 'ggg' => ['getPaddedUnit', ['weekYear', 3]], - 'gggg' => ['getPaddedUnit', ['weekYear', 4]], - 'ggggg' => ['getPaddedUnit', ['weekYear', 5]], - 'W' => 'isoWeek', - 'WW' => ['getPaddedUnit', ['isoWeek']], - 'Wo' => ['ordinal', ['isoWeek', 'W']], - 'w' => 'week', - 'ww' => ['getPaddedUnit', ['week']], - 'wo' => ['ordinal', ['week', 'w']], - 'x' => ['valueOf', []], - 'X' => 'timestamp', - 'Y' => 'year', - 'YY' => ['rawFormat', ['y']], - 'YYYY' => ['getPaddedUnit', ['year', 4]], - 'YYYYY' => ['getPaddedUnit', ['year', 5]], - 'YYYYYY' => function (CarbonInterface $date) { - return ($date->year < 0 ? '' : '+').$date->getPaddedUnit('year', 6); - }, - 'z' => ['rawFormat', ['T']], - 'zz' => 'tzName', - 'Z' => ['getOffsetString', []], - 'ZZ' => ['getOffsetString', ['']], - ]; - } + return $month; + }, + 'MMMM' => static fn (CarbonInterface $date, $originalFormat = null) => $date->getTranslatedMonthName( + $originalFormat, + ), + 'Mo' => ['ordinal', ['month', 'M']], + 'Q' => 'quarter', + 'Qo' => ['ordinal', ['quarter', 'M']], + 'G' => 'isoWeekYear', + 'GG' => ['getPaddedUnit', ['isoWeekYear']], + 'GGG' => ['getPaddedUnit', ['isoWeekYear', 3]], + 'GGGG' => ['getPaddedUnit', ['isoWeekYear', 4]], + 'GGGGG' => ['getPaddedUnit', ['isoWeekYear', 5]], + 'g' => 'weekYear', + 'gg' => ['getPaddedUnit', ['weekYear']], + 'ggg' => ['getPaddedUnit', ['weekYear', 3]], + 'gggg' => ['getPaddedUnit', ['weekYear', 4]], + 'ggggg' => ['getPaddedUnit', ['weekYear', 5]], + 'W' => 'isoWeek', + 'WW' => ['getPaddedUnit', ['isoWeek']], + 'Wo' => ['ordinal', ['isoWeek', 'W']], + 'w' => 'week', + 'ww' => ['getPaddedUnit', ['week']], + 'wo' => ['ordinal', ['week', 'w']], + 'x' => ['valueOf', []], + 'X' => 'timestamp', + 'Y' => 'year', + 'YY' => ['rawFormat', ['y']], + 'YYYY' => ['getPaddedUnit', ['year', 4]], + 'YYYYY' => ['getPaddedUnit', ['year', 5]], + 'YYYYYY' => static fn (CarbonInterface $date) => ($date->year < 0 ? '' : '+'). + $date->getPaddedUnit('year', 6), + 'z' => ['rawFormat', ['T']], + 'zz' => 'tzName', + 'Z' => ['getOffsetString', []], + 'ZZ' => ['getOffsetString', ['']], + ]; return $units; } @@ -2081,21 +2155,14 @@ public static function getIsoUnits() * @param int $length Length of the output (2 by default) * @param string $padString String to use for padding ("0" by default) * @param int $padType Side(s) to pad (STR_PAD_LEFT by default) - * - * @return string */ - public function getPaddedUnit($unit, $length = 2, $padString = '0', $padType = STR_PAD_LEFT) + public function getPaddedUnit($unit, $length = 2, $padString = '0', $padType = STR_PAD_LEFT): string { return ($this->$unit < 0 ? '-' : '').str_pad((string) abs($this->$unit), $length, $padString, $padType); } /** * Return a property with its ordinal. - * - * @param string $key - * @param string|null $period - * - * @return string */ public function ordinal(string $key, ?string $period = null): string { @@ -2112,13 +2179,11 @@ public function ordinal(string $key, ?string $period = null): string * Return the meridiem of the current time in the current locale. * * @param bool $isLower if true, returns lowercase variant if available in the current locale. - * - * @return string */ public function meridiem(bool $isLower = false): string { $hour = $this->hour; - $index = $hour < 12 ? 0 : 1; + $index = $hour < static::HOURS_PER_DAY / 2 ? 0 : 1; if ($isLower) { $key = 'meridiem.'.($index + 2); @@ -2152,27 +2217,22 @@ public function meridiem(bool $isLower = false): string * Returns the alternative number for a given date property if available in the current locale. * * @param string $key date property - * - * @return string */ public function getAltNumber(string $key): string { - return $this->translateNumber(\strlen($key) > 1 ? $this->$key : $this->rawFormat('h')); + return $this->translateNumber((int) (\strlen($key) > 1 ? $this->$key : $this->rawFormat($key))); } /** * Format in the current language using ISO replacement patterns. * - * @param string $format * @param string|null $originalFormat provide context if a chunk has been passed alone - * - * @return string */ public function isoFormat(string $format, ?string $originalFormat = null): string { $result = ''; $length = mb_strlen($format); - $originalFormat = $originalFormat ?: $format; + $originalFormat ??= $format; $inEscaped = false; $formats = null; $units = null; @@ -2214,10 +2274,8 @@ public function isoFormat(string $format, ?string $originalFormat = null): strin $code = $match[0]; $sequence = $formats[$code] ?? preg_replace_callback( '/MMMM|MM|DD|dddd/', - function ($code) { - return mb_substr($code[0], 1); - }, - $formats[strtoupper($code)] ?? '' + static fn ($code) => mb_substr($code[0], 1), + $formats[strtoupper($code)] ?? '', ); $rest = mb_substr($format, $i + mb_strlen($code)); $format = mb_substr($format, 0, $i).$sequence.$rest; @@ -2239,7 +2297,7 @@ function ($code) { } elseif (\is_array($sequence)) { try { $sequence = $this->{$sequence[0]}(...$sequence[1]); - } catch (ReflectionException | InvalidArgumentException | BadMethodCallException $e) { + } catch (ReflectionException | InvalidArgumentException | BadMethodCallException) { $sequence = ''; } } elseif (\is_string($sequence)) { @@ -2260,59 +2318,51 @@ function ($code) { /** * List of replacements from date() format to isoFormat(). - * - * @return array */ - public static function getFormatsToIsoReplacements() + public static function getFormatsToIsoReplacements(): array { static $replacements = null; - if ($replacements === null) { - $replacements = [ - 'd' => true, - 'D' => 'ddd', - 'j' => true, - 'l' => 'dddd', - 'N' => true, - 'S' => function ($date) { - $day = $date->rawFormat('j'); - - return str_replace((string) $day, '', $date->isoFormat('Do')); - }, - 'w' => true, - 'z' => true, - 'W' => true, - 'F' => 'MMMM', - 'm' => true, - 'M' => 'MMM', - 'n' => true, - 't' => true, - 'L' => true, - 'o' => true, - 'Y' => true, - 'y' => true, - 'a' => 'a', - 'A' => 'A', - 'B' => true, - 'g' => true, - 'G' => true, - 'h' => true, - 'H' => true, - 'i' => true, - 's' => true, - 'u' => true, - 'v' => true, - 'E' => true, - 'I' => true, - 'O' => true, - 'P' => true, - 'Z' => true, - 'c' => true, - 'r' => true, - 'U' => true, - 'T' => true, - ]; - } + $replacements ??= [ + 'd' => true, + 'D' => 'ddd', + 'j' => true, + 'l' => 'dddd', + 'N' => true, + 'S' => static fn ($date) => str_replace((string) $date->rawFormat('j'), '', $date->isoFormat('Do')), + 'w' => true, + 'z' => true, + 'W' => true, + 'F' => 'MMMM', + 'm' => true, + 'M' => 'MMM', + 'n' => true, + 't' => true, + 'L' => true, + 'o' => true, + 'Y' => true, + 'y' => true, + 'a' => 'a', + 'A' => 'A', + 'B' => true, + 'g' => true, + 'G' => true, + 'h' => true, + 'H' => true, + 'i' => true, + 's' => true, + 'u' => true, + 'v' => true, + 'E' => true, + 'I' => true, + 'O' => true, + 'P' => true, + 'Z' => true, + 'c' => true, + 'r' => true, + 'U' => true, + 'T' => true, + ]; return $replacements; } @@ -2320,10 +2370,6 @@ public static function getFormatsToIsoReplacements() /** * Format as ->format() do (using date replacements patterns from https://php.net/manual/en/function.date.php) * but translate words whenever possible (months, day names, etc.) using the current locale. - * - * @param string $format - * - * @return string */ public function translatedFormat(string $format): string { @@ -2405,33 +2451,18 @@ public function translatedFormat(string $format): string * like "-12:00". * * @param string $separator string to place between hours and minutes (":" by default) - * - * @return string */ - public function getOffsetString($separator = ':') + public function getOffsetString(string $separator = ':'): string { $second = $this->getOffset(); $symbol = $second < 0 ? '-' : '+'; $minute = abs($second) / static::SECONDS_PER_MINUTE; - $hour = str_pad((string) floor($minute / static::MINUTES_PER_HOUR), 2, '0', STR_PAD_LEFT); - $minute = str_pad((string) (((int) $minute) % static::MINUTES_PER_HOUR), 2, '0', STR_PAD_LEFT); + $hour = self::floorZeroPad($minute / static::MINUTES_PER_HOUR, 2); + $minute = self::floorZeroPad(((int) $minute) % static::MINUTES_PER_HOUR, 2); return "$symbol$hour$separator$minute"; } - protected static function executeStaticCallable($macro, ...$parameters) - { - return static::bindMacroContext(null, function () use (&$macro, &$parameters) { - if ($macro instanceof Closure) { - $boundMacro = @Closure::bind($macro, null, static::class); - - return ($boundMacro ?: $macro)(...$parameters); - } - - return $macro(...$parameters); - }); - } - /** * Dynamically handle calls to the class. * @@ -2439,48 +2470,61 @@ protected static function executeStaticCallable($macro, ...$parameters) * @param array $parameters parameters list * * @throws BadMethodCallException - * - * @return mixed */ - public static function __callStatic($method, $parameters) + public static function __callStatic(string $method, array $parameters): mixed { if (!static::hasMacro($method)) { foreach (static::getGenericMacros() as $callback) { try { return static::executeStaticCallable($callback, $method, ...$parameters); - } catch (BadMethodCallException $exception) { + } catch (BadMethodCallException) { continue; } } + if (static::isStrictModeEnabled()) { - throw new UnknownMethodException(sprintf('%s::%s', static::class, $method)); + throw new UnknownMethodException(\sprintf('%s::%s', static::class, $method)); } return null; } - return static::executeStaticCallable(static::$globalMacros[$method], ...$parameters); + return static::executeStaticCallable(static::getMacro($method), ...$parameters); } /** * Set specified unit to new given value. * - * @param string $unit year, month, day, hour, minute, second or microsecond - * @param int $value new value for given unit - * - * @return static + * @param string $unit year, month, day, hour, minute, second or microsecond + * @param Month|int $value new value for given unit */ - public function setUnit($unit, $value = null) + public function setUnit(string $unit, Month|int|float|null $value = null): static { + if (\is_float($value)) { + $int = (int) $value; + + if ((float) $int !== $value) { + throw new InvalidArgumentException( + "$unit cannot be changed to float value $value, integer expected", + ); + } + + $value = $int; + } + $unit = static::singularUnit($unit); + $value = self::monthToInt($value, $unit); $dateUnits = ['year', 'month', 'day']; + if (\in_array($unit, $dateUnits)) { - return $this->setDate(...array_map(function ($name) use ($unit, $value) { - return (int) ($name === $unit ? $value : $this->$name); - }, $dateUnits)); + return $this->setDate(...array_map( + fn ($name) => (int) ($name === $unit ? $value : $this->$name), + $dateUnits, + )); } $units = ['hour', 'minute', 'second', 'micro']; + if ($unit === 'millisecond' || $unit === 'milli') { $value *= 1000; $unit = 'micro'; @@ -2488,93 +2532,244 @@ public function setUnit($unit, $value = null) $unit = 'micro'; } - return $this->setTime(...array_map(function ($name) use ($unit, $value) { - return (int) ($name === $unit ? $value : $this->$name); - }, $units)); + return $this->setTime(...array_map( + fn ($name) => (int) ($name === $unit ? $value : $this->$name), + $units, + )); } /** * Returns standardized singular of a given singular/plural unit name (in English). - * - * @param string $unit - * - * @return string */ public static function singularUnit(string $unit): string { $unit = rtrim(mb_strtolower($unit), 's'); - if ($unit === 'centurie') { - return 'century'; - } + return match ($unit) { + 'centurie' => 'century', + 'millennia' => 'millennium', + default => $unit, + }; + } - if ($unit === 'millennia') { - return 'millennium'; + /** + * Returns standardized plural of a given singular/plural unit name (in English). + */ + public static function pluralUnit(string $unit): string + { + $unit = rtrim(strtolower($unit), 's'); + + return match ($unit) { + 'century' => 'centuries', + 'millennium', 'millennia' => 'millennia', + default => "{$unit}s", + }; + } + + public static function sleep(int|float $seconds): void + { + if (static::hasTestNow()) { + static::setTestNow(static::getTestNow()->avoidMutation()->addSeconds($seconds)); + + return; } - return $unit; + (new NativeClock('UTC'))->sleep($seconds); } /** - * Returns standardized plural of a given singular/plural unit name (in English). + * Dynamically handle calls to the class. * - * @param string $unit + * @param string $method magic method name called + * @param array $parameters parameters list * - * @return string + * @throws UnknownMethodException|BadMethodCallException|ReflectionException|Throwable */ - public static function pluralUnit(string $unit): string + public function __call(string $method, array $parameters): mixed { - $unit = rtrim(strtolower($unit), 's'); + $unit = rtrim($method, 's'); - if ($unit === 'century') { - return 'centuries'; + return $this->callDiffAlias($unit, $parameters) + ?? $this->callHumanDiffAlias($unit, $parameters) + ?? $this->callRoundMethod($unit, $parameters) + ?? $this->callIsMethod($unit, $parameters) + ?? $this->callModifierMethod($unit, $parameters) + ?? $this->callPeriodMethod($method, $parameters) + ?? $this->callGetOrSetMethod($method, $parameters) + ?? $this->callMacroMethod($method, $parameters); + } + + /** + * Return the Carbon instance passed through, a now instance in the same timezone + * if null given or parse the input if string given. + */ + protected function resolveCarbon(DateTimeInterface|string|null $date): self + { + if (!$date) { + return $this->nowWithSameTz(); } - if ($unit === 'millennium' || $unit === 'millennia') { - return 'millennia'; + if (\is_string($date)) { + return $this->transmitFactory(fn () => static::parse($date, $this->getTimezone())); } - return "{$unit}s"; + return $date instanceof self ? $date : $this->transmitFactory(static fn () => static::instance($date)); + } + + protected static function weekRotate(int $day, int $rotation): int + { + return (static::DAYS_PER_WEEK + $rotation % static::DAYS_PER_WEEK + $day) % static::DAYS_PER_WEEK; } - protected function executeCallable($macro, ...$parameters) + protected function executeCallable(callable $macro, ...$parameters) { if ($macro instanceof Closure) { $boundMacro = @$macro->bindTo($this, static::class) ?: @$macro->bindTo(null, static::class); - return ($boundMacro ?: $macro)(...$parameters); + return \call_user_func_array($boundMacro ?: $macro, $parameters); } - return $macro(...$parameters); + return \call_user_func_array($macro, $parameters); } - protected function executeCallableWithContext($macro, ...$parameters) + protected function executeCallableWithContext(callable $macro, ...$parameters) { return static::bindMacroContext($this, function () use (&$macro, &$parameters) { return $this->executeCallable($macro, ...$parameters); }); } - protected static function getGenericMacros() + protected function getAllGenericMacros(): Generator { - foreach (static::$globalGenericMacros as $list) { + yield from $this->localGenericMacros ?? []; + yield from $this->transmitFactory(static fn () => static::getGenericMacros()); + } + + protected static function getGenericMacros(): Generator + { + foreach ((FactoryImmutable::getInstance()->getSettings()['genericMacros'] ?? []) as $list) { foreach ($list as $macro) { yield $macro; } } } - /** - * Dynamically handle calls to the class. - * - * @param string $method magic method name called - * @param array $parameters parameters list - * - * @throws UnknownMethodException|BadMethodCallException|ReflectionException|Throwable - * - * @return mixed - */ - public function __call($method, $parameters) + protected static function executeStaticCallable(callable $macro, ...$parameters) + { + return static::bindMacroContext(null, function () use (&$macro, &$parameters) { + if ($macro instanceof Closure) { + $boundMacro = @Closure::bind($macro, null, static::class); + + return \call_user_func_array($boundMacro ?: $macro, $parameters); + } + + return \call_user_func_array($macro, $parameters); + }); + } + + protected function getTranslatedFormByRegExp($baseKey, $keySuffix, $context, $subKey, $defaultValue) + { + $key = $baseKey.$keySuffix; + $standaloneKey = "{$key}_standalone"; + $baseTranslation = $this->getTranslationMessage($key); + + if ($baseTranslation instanceof Closure) { + return $baseTranslation($this, $context, $subKey) ?: $defaultValue; + } + + if ( + $this->getTranslationMessage("$standaloneKey.$subKey") && + (!$context || (($regExp = $this->getTranslationMessage("{$baseKey}_regexp")) && !preg_match($regExp, $context))) + ) { + $key = $standaloneKey; + } + + return $this->getTranslationMessage("$key.$subKey", null, $defaultValue); + } + + private function callGetOrSetMethod(string $method, array $parameters): mixed + { + if (preg_match('/^([a-z]{2,})(In|Of)([A-Z][a-z]+)$/', $method)) { + $localStrictModeEnabled = $this->localStrictModeEnabled; + $this->localStrictModeEnabled = true; + + try { + return $this->callGetOrSet($method, $parameters[0] ?? null); + } catch (UnknownGetterException|UnknownSetterException|ImmutableException) { + // continue to macro + } finally { + $this->localStrictModeEnabled = $localStrictModeEnabled; + } + } + + return null; + } + + private function callGetOrSet(string $name, mixed $value): mixed + { + if ($value !== null) { + if (\is_string($value) || \is_int($value) || \is_float($value) || $value instanceof DateTimeZone || $value instanceof Month) { + return $this->set($name, $value); + } + + return null; + } + + return $this->get($name); + } + + private function getUTCUnit(string $unit): ?string + { + if (str_starts_with($unit, 'Real')) { + return substr($unit, 4); + } + + if (str_starts_with($unit, 'UTC')) { + return substr($unit, 3); + } + + return null; + } + + private function callDiffAlias(string $method, array $parameters): mixed + { + if (preg_match('/^(diff|floatDiff)In(Real|UTC|Utc)?(.+)$/', $method, $match)) { + $mode = strtoupper($match[2] ?? ''); + $betterMethod = $match[1] === 'floatDiff' ? str_replace('floatDiff', 'diff', $method) : null; + + if ($mode === 'REAL') { + $mode = 'UTC'; + $betterMethod = str_replace($match[2], 'UTC', $betterMethod ?? $method); + } + + if ($betterMethod) { + @trigger_error( + "Use the method $betterMethod instead to make it more explicit about what it does.\n". + 'On next major version, "float" prefix will be removed (as all diff are now returning floating numbers)'. + ' and "Real" methods will be removed in favor of "UTC" because what it actually does is to convert both'. + ' dates to UTC timezone before comparison, while by default it does it only if both dates don\'t have'. + ' exactly the same timezone (Note: 2 timezones with the same offset but different names are considered'. + " different as it's not safe to assume they will always have the same offset).", + \E_USER_DEPRECATED, + ); + } + + $unit = self::pluralUnit($match[3]); + $diffMethod = 'diffIn'.ucfirst($unit); + + if (\in_array($unit, ['days', 'weeks', 'months', 'quarters', 'years'])) { + $parameters['utc'] = ($mode === 'UTC'); + } + + if (method_exists($this, $diffMethod)) { + return $this->$diffMethod(...$parameters); + } + } + + return null; + } + + private function callHumanDiffAlias(string $method, array $parameters): ?string { $diffSizes = [ // @mode diffForHumans @@ -2595,7 +2790,7 @@ public function __call($method, $parameters) $sizePattern = implode('|', array_keys($diffSizes)); $syntaxPattern = implode('|', array_keys($diffSyntaxModes)); - if (preg_match("/^(?$sizePattern)(?$syntaxPattern)DiffForHumans$/", $method, $match)) { + if (preg_match("/^(?$sizePattern)(?$syntaxPattern)DiffForHuman$/", $method, $match)) { $dates = array_filter($parameters, function ($parameter) { return $parameter instanceof DateTimeInterface; }); @@ -2610,38 +2805,73 @@ public function __call($method, $parameters) return $this->diffForHumans($other, $diffSyntaxModes[$match['syntax']], $diffSizes[$match['size']], ...$parameters); } - $roundedValue = $this->callRoundMethod($method, $parameters); + return null; + } - if ($roundedValue !== null) { - return $roundedValue; + private function callIsMethod(string $unit, array $parameters): ?bool + { + if (!str_starts_with($unit, 'is')) { + return null; } - $unit = rtrim($method, 's'); + $word = substr($unit, 2); + + if (\in_array($word, static::$days, true)) { + return $this->isDayOfWeek($word); + } + + return match ($word) { + // @call is Check if the current instance has UTC timezone. (Both isUtc and isUTC cases are valid.) + 'Utc', 'UTC' => $this->utc, + // @call is Check if the current instance has non-UTC timezone. + 'Local' => $this->local, + // @call is Check if the current instance is a valid date. + 'Valid' => $this->year !== 0, + // @call is Check if the current instance is in a daylight saving time. + 'DST' => $this->dst, + default => $this->callComparatorMethod($word, $parameters), + }; + } + + private function callComparatorMethod(string $unit, array $parameters): ?bool + { + $start = substr($unit, 0, 4); + $factor = -1; - if (str_starts_with($unit, 'is')) { - $word = substr($unit, 2); + if ($start === 'Last') { + $start = 'Next'; + $factor = 1; + } + + if ($start === 'Next') { + $lowerUnit = strtolower(substr($unit, 4)); + + if (static::isModifiableUnit($lowerUnit)) { + return $this->avoidMutation()->addUnit($lowerUnit, $factor, false)->isSameUnit($lowerUnit, ...($parameters ?: ['now'])); + } + } - if (\in_array($word, static::$days, true)) { - return $this->isDayOfWeek($word); + if ($start === 'Same') { + try { + return $this->isSameUnit(strtolower(substr($unit, 4)), ...$parameters); + } catch (BadComparisonUnitException) { + // Try next } + } - switch ($word) { - // @call is Check if the current instance has UTC timezone. (Both isUtc and isUTC cases are valid.) - case 'Utc': - case 'UTC': - return $this->utc; - // @call is Check if the current instance has non-UTC timezone. - case 'Local': - return $this->local; - // @call is Check if the current instance is a valid date. - case 'Valid': - return $this->year !== 0; - // @call is Check if the current instance is in a daylight saving time. - case 'DST': - return $this->dst; + if (str_starts_with($unit, 'Current')) { + try { + return $this->isCurrentUnit(strtolower(substr($unit, 7))); + } catch (BadComparisonUnitException | BadMethodCallException) { + // Try next } } + return null; + } + + private function callModifierMethod(string $unit, array $parameters): ?static + { $action = substr($unit, 0, 3); $overflow = null; @@ -2655,11 +2885,12 @@ public function __call($method, $parameters) if ($action === 'add' || $action === 'sub') { $unit = substr($unit, 3); + $utcUnit = $this->getUTCUnit($unit); - if (str_starts_with($unit, 'Real')) { - $unit = static::singularUnit(substr($unit, 4)); + if ($utcUnit) { + $unit = static::singularUnit($utcUnit); - return $this->{"{$action}RealUnit"}($unit, ...$parameters); + return $this->{"{$action}UTCUnit"}($unit, ...$parameters); } if (preg_match('/^(Month|Quarter|Year|Decade|Century|Centurie|Millennium|Millennia)s?(No|With|Without|WithNo)Overflow$/', $unit, $match)) { @@ -2674,38 +2905,11 @@ public function __call($method, $parameters) return $this->{"{$action}Unit"}($unit, $this->getMagicParameter($parameters, 0, 'value', 1), $overflow); } - $sixFirstLetters = substr($unit, 0, 6); - $factor = -1; - - if ($sixFirstLetters === 'isLast') { - $sixFirstLetters = 'isNext'; - $factor = 1; - } - - if ($sixFirstLetters === 'isNext') { - $lowerUnit = strtolower(substr($unit, 6)); - - if (static::isModifiableUnit($lowerUnit)) { - return $this->copy()->addUnit($lowerUnit, $factor, false)->isSameUnit($lowerUnit, ...$parameters); - } - } - - if ($sixFirstLetters === 'isSame') { - try { - return $this->isSameUnit(strtolower(substr($unit, 6)), ...$parameters); - } catch (BadComparisonUnitException $exception) { - // Try next - } - } - - if (str_starts_with($unit, 'isCurrent')) { - try { - return $this->isCurrentUnit(strtolower(substr($unit, 9))); - } catch (BadComparisonUnitException | BadMethodCallException $exception) { - // Try next - } - } + return null; + } + private function callPeriodMethod(string $method, array $parameters): ?CarbonPeriod + { if (str_ends_with($method, 'Until')) { try { $unit = static::singularUnit(substr($method, 0, -5)); @@ -2715,26 +2919,29 @@ public function __call($method, $parameters) $this->getMagicParameter($parameters, 1, 'factor', 1), $unit ); - } catch (InvalidArgumentException $exception) { + } catch (InvalidArgumentException) { // Try macros } } + return null; + } + + private function callMacroMethod(string $method, array $parameters): mixed + { return static::bindMacroContext($this, function () use (&$method, &$parameters) { $macro = $this->getLocalMacro($method); if (!$macro) { - foreach ([$this->localGenericMacros ?: [], static::getGenericMacros()] as $list) { - foreach ($list as $callback) { - try { - return $this->executeCallable($callback, $method, ...$parameters); - } catch (BadMethodCallException $exception) { - continue; - } + foreach ($this->getAllGenericMacros() as $callback) { + try { + return $this->executeCallable($callback, $method, ...$parameters); + } catch (BadMethodCallException) { + continue; } } - if ($this->localStrictModeEnabled ?? static::isStrictModeEnabled()) { + if ($this->isLocalStrictModeEnabled()) { throw new UnknownMethodException($method); } @@ -2744,4 +2951,23 @@ public function __call($method, $parameters) return $this->executeCallable($macro, ...$parameters); }); } + + private static function floorZeroPad(int|float $value, int $length): string + { + return str_pad((string) floor($value), $length, '0', STR_PAD_LEFT); + } + + /** + * @template T of CarbonInterface + * + * @param T $date + * + * @return T + */ + private function mutateIfMutable(CarbonInterface $date): CarbonInterface + { + return $this instanceof DateTimeImmutable + ? $date + : $this->modify('@'.$date->rawFormat('U.u'))->setTimezone($date->getTimezone()); + } } diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/DeprecatedPeriodProperties.php b/vendor/nesbot/carbon/src/Carbon/Traits/DeprecatedPeriodProperties.php new file mode 100644 index 000000000..71660c3a8 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/DeprecatedPeriodProperties.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +use Carbon\CarbonInterface; +use Carbon\CarbonInterval; + +trait DeprecatedPeriodProperties +{ + /** + * Period start in PHP < 8.2. + * + * @var CarbonInterface + * + * @deprecated PHP 8.2 this property is no longer in sync with the actual period start. + */ + public $start; + + /** + * Period end in PHP < 8.2. + * + * @var CarbonInterface|null + * + * @deprecated PHP 8.2 this property is no longer in sync with the actual period end. + */ + public $end; + + /** + * Period current iterated date in PHP < 8.2. + * + * @var CarbonInterface|null + * + * @deprecated PHP 8.2 this property is no longer in sync with the actual period current iterated date. + */ + public $current; + + /** + * Period interval in PHP < 8.2. + * + * @var CarbonInterval|null + * + * @deprecated PHP 8.2 this property is no longer in sync with the actual period interval. + */ + public $interval; + + /** + * Period recurrences in PHP < 8.2. + * + * @var int|float|null + * + * @deprecated PHP 8.2 this property is no longer in sync with the actual period recurrences. + */ + public $recurrences; + + /** + * Period start included option in PHP < 8.2. + * + * @var bool + * + * @deprecated PHP 8.2 this property is no longer in sync with the actual period start included option. + */ + public $include_start_date; + + /** + * Period end included option in PHP < 8.2. + * + * @var bool + * + * @deprecated PHP 8.2 this property is no longer in sync with the actual period end included option. + */ + public $include_end_date; +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/DeprecatedProperties.php b/vendor/nesbot/carbon/src/Carbon/Traits/DeprecatedProperties.php deleted file mode 100644 index 5acc6f5c7..000000000 --- a/vendor/nesbot/carbon/src/Carbon/Traits/DeprecatedProperties.php +++ /dev/null @@ -1,61 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Carbon\Traits; - -trait DeprecatedProperties -{ - /** - * the day of week in current locale LC_TIME - * - * @var string - * - * @deprecated It uses OS language package and strftime() which is deprecated since PHP 8.1. - * Use ->isoFormat('MMM') instead. - * Deprecated since 2.55.0 - */ - public $localeDayOfWeek; - - /** - * the abbreviated day of week in current locale LC_TIME - * - * @var string - * - * @deprecated It uses OS language package and strftime() which is deprecated since PHP 8.1. - * Use ->isoFormat('dddd') instead. - * Deprecated since 2.55.0 - */ - public $shortLocaleDayOfWeek; - - /** - * the month in current locale LC_TIME - * - * @var string - * - * @deprecated It uses OS language package and strftime() which is deprecated since PHP 8.1. - * Use ->isoFormat('ddd') instead. - * Deprecated since 2.55.0 - */ - public $localeMonth; - - /** - * the abbreviated month in current locale LC_TIME - * - * @var string - * - * @deprecated It uses OS language package and strftime() which is deprecated since PHP 8.1. - * Use ->isoFormat('MMMM') instead. - * Deprecated since 2.55.0 - */ - public $shortLocaleMonth; -} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Difference.php b/vendor/nesbot/carbon/src/Carbon/Traits/Difference.php index ab5b65d23..db5fe1d27 100644 --- a/vendor/nesbot/carbon/src/Carbon/Traits/Difference.php +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Difference.php @@ -1,5 +1,7 @@ s !== 0 || $diff->i !== 0 || $diff->h !== 0 || $diff->d !== 0 || $diff->m !== 0 || $diff->y !== 0) { - $diff->f = (round($diff->f * 1000000) + 1000000) / 1000000; - $diff->s--; - - if ($diff->s < 0) { - $diff->s += 60; - $diff->i--; - - if ($diff->i < 0) { - $diff->i += 60; - $diff->h--; - - if ($diff->h < 0) { - $diff->h += 24; - $diff->d--; - - if ($diff->d < 0) { - $diff->d += 30; - $diff->m--; - - if ($diff->m < 0) { - $diff->m += 12; - $diff->y--; - } - } - } - } - } - - return; - } - - $diff->f *= -1; - $diff->invert(); - } - - /** - * @param DateInterval $diff - * @param bool $absolute - * - * @return CarbonInterval - */ - protected static function fixDiffInterval(DateInterval $diff, $absolute, array $skip = []) - { - $diff = CarbonInterval::instance($diff, $skip); - - // Work-around for https://bugs.php.net/bug.php?id=77145 - // @codeCoverageIgnoreStart - if ($diff->f > 0 && $diff->y === -1 && $diff->m === 11 && $diff->d >= 27 && $diff->h === 23 && $diff->i === 59 && $diff->s === 59) { - $diff->y = 0; - $diff->m = 0; - $diff->d = 0; - $diff->h = 0; - $diff->i = 0; - $diff->s = 0; - $diff->f = (1000000 - round($diff->f * 1000000)) / 1000000; - $diff->invert(); - } elseif ($diff->f < 0) { - static::fixNegativeMicroseconds($diff); - } - // @codeCoverageIgnoreEnd - - if ($absolute && $diff->invert) { - $diff->invert(); - } - - return $diff; - } - /** * Get the difference as a DateInterval instance. * Return relative interval (negative if $absolute flag is not set to true and the given date is before @@ -120,8 +45,7 @@ protected static function fixDiffInterval(DateInterval $diff, $absolute, array $ * * @return DateInterval */ - #[ReturnTypeWillChange] - public function diff($date = null, $absolute = false) + public function diffAsDateInterval($date = null, bool $absolute = false): DateInterval { $other = $this->resolveCarbon($date); @@ -129,13 +53,11 @@ public function diff($date = null, $absolute = false) // It was initially introduced for https://bugs.php.net/bug.php?id=80998 // The very specific case of 80998 was fixed in PHP 8.1beta3, but it introduced 81458 // So we still need to keep this for now - // @codeCoverageIgnoreStart - if (version_compare(PHP_VERSION, '8.1.0-dev', '>=') && $other->tz !== $this->tz) { - $other = $other->avoidMutation()->tz($this->tz); + if ($other->tz !== $this->tz) { + $other = $other->avoidMutation()->setTimezone($this->tz); } - // @codeCoverageIgnoreEnd - return parent::diff($other, (bool) $absolute); + return parent::diff($other, $absolute); } /** @@ -148,633 +70,391 @@ public function diff($date = null, $absolute = false) * * @return CarbonInterval */ - public function diffAsCarbonInterval($date = null, $absolute = true, array $skip = []) + public function diffAsCarbonInterval($date = null, bool $absolute = false, array $skip = []): CarbonInterval { - return static::fixDiffInterval($this->diff($this->resolveCarbon($date), $absolute), $absolute, $skip); + return CarbonInterval::diff($this, $this->resolveCarbon($date), $absolute, $skip) + ->setLocalTranslator($this->getLocalTranslator()); } /** - * Get the difference in years - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference + * @alias diffAsCarbonInterval * - * @return int - */ - public function diffInYears($date = null, $absolute = true) - { - return (int) $this->diff($this->resolveCarbon($date), $absolute)->format('%r%y'); - } - - /** - * Get the difference in quarters rounded down. + * Get the difference as a DateInterval instance. + * Return relative interval (negative if $absolute flag is not set to true and the given date is before + * current one). * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference * - * @return int + * @return CarbonInterval */ - public function diffInQuarters($date = null, $absolute = true) + public function diff($date = null, bool $absolute = false, array $skip = []): CarbonInterval { - return (int) ($this->diffInMonths($date, $absolute) / static::MONTHS_PER_QUARTER); + return $this->diffAsCarbonInterval($date, $absolute, $skip); } /** - * Get the difference in months rounded down. - * + * @param Unit|string $unit microsecond, millisecond, second, minute, + * hour, day, week, month, quarter, year, + * century, millennium * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference + * @param bool $utc Always convert dates to UTC before comparing (if not set, it will do it only if timezones are different) * - * @return int + * @return float */ - public function diffInMonths($date = null, $absolute = true) + public function diffInUnit(Unit|string $unit, $date = null, bool $absolute = false, bool $utc = false): float { - $date = $this->resolveCarbon($date)->avoidMutation()->tz($this->tz); - - [$yearStart, $monthStart, $dayStart] = explode('-', $this->format('Y-m-dHisu')); - [$yearEnd, $monthEnd, $dayEnd] = explode('-', $date->format('Y-m-dHisu')); + $unit = static::pluralUnit($unit instanceof Unit ? $unit->value : rtrim($unit, 'z')); + $method = 'diffIn'.$unit; - $diff = (((int) $yearEnd) - ((int) $yearStart)) * static::MONTHS_PER_YEAR + - ((int) $monthEnd) - ((int) $monthStart); - - if ($diff > 0) { - $diff -= ($dayStart > $dayEnd ? 1 : 0); - } elseif ($diff < 0) { - $diff += ($dayStart < $dayEnd ? 1 : 0); + if (!method_exists($this, $method)) { + throw new UnknownUnitException($unit); } - return $absolute ? abs($diff) : $diff; + return $this->$method($date, $absolute, $utc); } /** - * Get the difference in weeks rounded down. - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return int - */ - public function diffInWeeks($date = null, $absolute = true) - { - return (int) ($this->diffInDays($date, $absolute) / static::DAYS_PER_WEEK); - } - - /** - * Get the difference in days rounded down. - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return int - */ - public function diffInDays($date = null, $absolute = true) - { - return $this->getIntervalDayDiff($this->diff($this->resolveCarbon($date), $absolute)); - } - - /** - * Get the difference in days using a filter closure rounded down. - * - * @param Closure $callback - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return int - */ - public function diffInDaysFiltered(Closure $callback, $date = null, $absolute = true) - { - return $this->diffFiltered(CarbonInterval::day(), $callback, $date, $absolute); - } - - /** - * Get the difference in hours using a filter closure rounded down. - * - * @param Closure $callback - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return int - */ - public function diffInHoursFiltered(Closure $callback, $date = null, $absolute = true) - { - return $this->diffFiltered(CarbonInterval::hour(), $callback, $date, $absolute); - } - - /** - * Get the difference by the given interval using a filter closure. + * Get the difference in years * - * @param CarbonInterval $ci An interval to traverse by - * @param Closure $callback * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference + * @param bool $utc Always convert dates to UTC before comparing (if not set, it will do it only if timezones are different) * - * @return int + * @return float */ - public function diffFiltered(CarbonInterval $ci, Closure $callback, $date = null, $absolute = true) + public function diffInYears($date = null, bool $absolute = false, bool $utc = false): float { $start = $this; $end = $this->resolveCarbon($date); - $inverse = false; - if ($end < $start) { - $start = $end; - $end = $this; - $inverse = true; + if ($utc) { + $start = $start->avoidMutation()->utc(); + $end = $end->avoidMutation()->utc(); } - $options = CarbonPeriod::EXCLUDE_END_DATE | ($this->isMutable() ? 0 : CarbonPeriod::IMMUTABLE); - $diff = $ci->toPeriod($start, $end, $options)->filter($callback)->count(); + $ascending = ($start <= $end); + $sign = $absolute || $ascending ? 1 : -1; - return $inverse && !$absolute ? -$diff : $diff; - } + if (!$ascending) { + [$start, $end] = [$end, $start]; + } - /** - * Get the difference in weekdays rounded down. - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return int - */ - public function diffInWeekdays($date = null, $absolute = true) - { - return $this->diffInDaysFiltered(static function (CarbonInterface $date) { - return $date->isWeekday(); - }, $this->resolveCarbon($date)->avoidMutation()->modify($this->format('H:i:s.u')), $absolute); - } + $yearsDiff = (int) $start->diff($end, $absolute)->format('%r%y'); + /** @var Carbon|CarbonImmutable $floorEnd */ + $floorEnd = $start->avoidMutation()->addYears($yearsDiff); - /** - * Get the difference in weekend days using a filter rounded down. - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return int - */ - public function diffInWeekendDays($date = null, $absolute = true) - { - return $this->diffInDaysFiltered(static function (CarbonInterface $date) { - return $date->isWeekend(); - }, $this->resolveCarbon($date)->avoidMutation()->modify($this->format('H:i:s.u')), $absolute); - } + if ($floorEnd >= $end) { + return $sign * $yearsDiff; + } - /** - * Get the difference in hours rounded down. - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return int - */ - public function diffInHours($date = null, $absolute = true) - { - return (int) ($this->diffInSeconds($date, $absolute) / static::SECONDS_PER_MINUTE / static::MINUTES_PER_HOUR); - } + /** @var Carbon|CarbonImmutable $ceilEnd */ + $ceilEnd = $start->avoidMutation()->addYears($yearsDiff + 1); - /** - * Get the difference in hours rounded down using timestamps. - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return int - */ - public function diffInRealHours($date = null, $absolute = true) - { - return (int) ($this->diffInRealSeconds($date, $absolute) / static::SECONDS_PER_MINUTE / static::MINUTES_PER_HOUR); - } + $daysToFloor = $floorEnd->diffInDays($end); + $daysToCeil = $end->diffInDays($ceilEnd); - /** - * Get the difference in minutes rounded down. - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return int - */ - public function diffInMinutes($date = null, $absolute = true) - { - return (int) ($this->diffInSeconds($date, $absolute) / static::SECONDS_PER_MINUTE); + return $sign * ($yearsDiff + $daysToFloor / ($daysToCeil + $daysToFloor)); } /** - * Get the difference in minutes rounded down using timestamps. + * Get the difference in quarters. * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference + * @param bool $utc Always convert dates to UTC before comparing (if not set, it will do it only if timezones are different) * - * @return int + * @return float */ - public function diffInRealMinutes($date = null, $absolute = true) + public function diffInQuarters($date = null, bool $absolute = false, bool $utc = false): float { - return (int) ($this->diffInRealSeconds($date, $absolute) / static::SECONDS_PER_MINUTE); + return $this->diffInMonths($date, $absolute, $utc) / static::MONTHS_PER_QUARTER; } /** - * Get the difference in seconds rounded down. + * Get the difference in months. * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference + * @param bool $utc Always convert dates to UTC before comparing (if not set, it will do it only if timezones are different) * - * @return int + * @return float */ - public function diffInSeconds($date = null, $absolute = true) + public function diffInMonths($date = null, bool $absolute = false, bool $utc = false): float { - $diff = $this->diff($date); + $start = $this; + $end = $this->resolveCarbon($date); - if ($diff->days === 0) { - $diff = static::fixDiffInterval($diff, $absolute); + // Compare using UTC + if ($utc || ($end->timezoneName !== $start->timezoneName)) { + $start = $start->avoidMutation()->utc(); + $end = $end->avoidMutation()->utc(); } - $value = (((($diff->m || $diff->y ? $diff->days : $diff->d) * static::HOURS_PER_DAY) + - $diff->h) * static::MINUTES_PER_HOUR + - $diff->i) * static::SECONDS_PER_MINUTE + - $diff->s; - - return $absolute || !$diff->invert ? $value : -$value; - } + [$yearStart, $monthStart, $dayStart] = explode('-', $start->format('Y-m-dHisu')); + [$yearEnd, $monthEnd, $dayEnd] = explode('-', $end->format('Y-m-dHisu')); - /** - * Get the difference in microseconds. - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return int - */ - public function diffInMicroseconds($date = null, $absolute = true) - { - $diff = $this->diff($date); - $value = (int) round(((((($diff->m || $diff->y ? $diff->days : $diff->d) * static::HOURS_PER_DAY) + - $diff->h) * static::MINUTES_PER_HOUR + - $diff->i) * static::SECONDS_PER_MINUTE + - ($diff->f + $diff->s)) * static::MICROSECONDS_PER_SECOND); - - return $absolute || !$diff->invert ? $value : -$value; - } + $monthsDiff = (((int) $yearEnd) - ((int) $yearStart)) * static::MONTHS_PER_YEAR + + ((int) $monthEnd) - ((int) $monthStart); - /** - * Get the difference in milliseconds rounded down. - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return int - */ - public function diffInMilliseconds($date = null, $absolute = true) - { - return (int) ($this->diffInMicroseconds($date, $absolute) / static::MICROSECONDS_PER_MILLISECOND); - } + if ($monthsDiff > 0) { + $monthsDiff -= ($dayStart > $dayEnd ? 1 : 0); + } elseif ($monthsDiff < 0) { + $monthsDiff += ($dayStart < $dayEnd ? 1 : 0); + } - /** - * Get the difference in seconds using timestamps. - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return int - */ - public function diffInRealSeconds($date = null, $absolute = true) - { - /** @var CarbonInterface $date */ - $date = $this->resolveCarbon($date); - $value = $date->getTimestamp() - $this->getTimestamp(); + $ascending = ($start <= $end); + $sign = $absolute || $ascending ? 1 : -1; + $monthsDiff = abs($monthsDiff); - return $absolute ? abs($value) : $value; - } + if (!$ascending) { + [$start, $end] = [$end, $start]; + } - /** - * Get the difference in microseconds using timestamps. - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return int - */ - public function diffInRealMicroseconds($date = null, $absolute = true) - { - /** @var CarbonInterface $date */ - $date = $this->resolveCarbon($date); - $value = ($date->timestamp - $this->timestamp) * static::MICROSECONDS_PER_SECOND + - $date->micro - $this->micro; + /** @var Carbon|CarbonImmutable $floorEnd */ + $floorEnd = $start->avoidMutation()->addMonths($monthsDiff); - return $absolute ? abs($value) : $value; - } + if ($floorEnd >= $end) { + return $sign * $monthsDiff; + } - /** - * Get the difference in milliseconds rounded down using timestamps. - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return int - */ - public function diffInRealMilliseconds($date = null, $absolute = true) - { - return (int) ($this->diffInRealMicroseconds($date, $absolute) / static::MICROSECONDS_PER_MILLISECOND); - } + /** @var Carbon|CarbonImmutable $ceilEnd */ + $ceilEnd = $start->avoidMutation()->addMonths($monthsDiff + 1); - /** - * Get the difference in seconds as float (microsecond-precision). - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return float - */ - public function floatDiffInSeconds($date = null, $absolute = true) - { - return (float) ($this->diffInMicroseconds($date, $absolute) / static::MICROSECONDS_PER_SECOND); - } + $daysToFloor = $floorEnd->diffInDays($end); + $daysToCeil = $end->diffInDays($ceilEnd); - /** - * Get the difference in minutes as float (microsecond-precision). - * - * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date - * @param bool $absolute Get the absolute of the difference - * - * @return float - */ - public function floatDiffInMinutes($date = null, $absolute = true) - { - return $this->floatDiffInSeconds($date, $absolute) / static::SECONDS_PER_MINUTE; + return $sign * ($monthsDiff + $daysToFloor / ($daysToCeil + $daysToFloor)); } /** - * Get the difference in hours as float (microsecond-precision). + * Get the difference in weeks. * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference + * @param bool $utc Always convert dates to UTC before comparing (if not set, it will do it only if timezones are different) * * @return float */ - public function floatDiffInHours($date = null, $absolute = true) + public function diffInWeeks($date = null, bool $absolute = false, bool $utc = false): float { - return $this->floatDiffInMinutes($date, $absolute) / static::MINUTES_PER_HOUR; + return $this->diffInDays($date, $absolute, $utc) / static::DAYS_PER_WEEK; } /** - * Get the difference in days as float (microsecond-precision). + * Get the difference in days. * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference + * @param bool $utc Always convert dates to UTC before comparing (if not set, it will do it only if timezones are different) * * @return float */ - public function floatDiffInDays($date = null, $absolute = true) + public function diffInDays($date = null, bool $absolute = false, bool $utc = false): float { - $hoursDiff = $this->floatDiffInHours($date, $absolute); - $interval = $this->diff($date, $absolute); + $date = $this->resolveCarbon($date); + $current = $this; - if ($interval->y === 0 && $interval->m === 0 && $interval->d === 0) { - return $hoursDiff / static::HOURS_PER_DAY; + // Compare using UTC + if ($utc || ($date->timezoneName !== $current->timezoneName)) { + $date = $date->avoidMutation()->utc(); + $current = $current->avoidMutation()->utc(); } - $daysDiff = $this->getIntervalDayDiff($interval); + $negative = ($date < $current); + [$start, $end] = $negative ? [$date, $current] : [$current, $date]; + $interval = $start->diffAsDateInterval($end); + $daysA = $this->getIntervalDayDiff($interval); + $floorEnd = $start->avoidMutation()->addDays($daysA); + $daysB = $daysA + ($floorEnd <= $end ? 1 : -1); + $ceilEnd = $start->avoidMutation()->addDays($daysB); + $microsecondsBetween = $floorEnd->diffInMicroseconds($ceilEnd); + $microsecondsToEnd = $floorEnd->diffInMicroseconds($end); - return $daysDiff + fmod($hoursDiff, static::HOURS_PER_DAY) / static::HOURS_PER_DAY; + return ($negative && !$absolute ? -1 : 1) + * ($daysA * ($microsecondsBetween - $microsecondsToEnd) + $daysB * $microsecondsToEnd) + / $microsecondsBetween; } /** - * Get the difference in weeks as float (microsecond-precision). + * Get the difference in days using a filter closure. * + * @param Closure $callback * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference * - * @return float + * @return int */ - public function floatDiffInWeeks($date = null, $absolute = true) + public function diffInDaysFiltered(Closure $callback, $date = null, bool $absolute = false): int { - return $this->floatDiffInDays($date, $absolute) / static::DAYS_PER_WEEK; + return $this->diffFiltered(CarbonInterval::day(), $callback, $date, $absolute); } /** - * Get the difference in months as float (microsecond-precision). + * Get the difference in hours using a filter closure. * + * @param Closure $callback * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference * - * @return float + * @return int */ - public function floatDiffInMonths($date = null, $absolute = true) + public function diffInHoursFiltered(Closure $callback, $date = null, bool $absolute = false): int { - $start = $this; - $end = $this->resolveCarbon($date); - $ascending = ($start <= $end); - $sign = $absolute || $ascending ? 1 : -1; - if (!$ascending) { - [$start, $end] = [$end, $start]; - } - $monthsDiff = $start->diffInMonths($end); - /** @var Carbon|CarbonImmutable $floorEnd */ - $floorEnd = $start->avoidMutation()->addMonths($monthsDiff); - - if ($floorEnd >= $end) { - return $sign * $monthsDiff; - } - - /** @var Carbon|CarbonImmutable $startOfMonthAfterFloorEnd */ - $startOfMonthAfterFloorEnd = $floorEnd->avoidMutation()->addMonth()->startOfMonth(); - - if ($startOfMonthAfterFloorEnd > $end) { - return $sign * ($monthsDiff + $floorEnd->floatDiffInDays($end) / $floorEnd->daysInMonth); - } - - return $sign * ($monthsDiff + $floorEnd->floatDiffInDays($startOfMonthAfterFloorEnd) / $floorEnd->daysInMonth + $startOfMonthAfterFloorEnd->floatDiffInDays($end) / $end->daysInMonth); + return $this->diffFiltered(CarbonInterval::hour(), $callback, $date, $absolute); } /** - * Get the difference in year as float (microsecond-precision). + * Get the difference by the given interval using a filter closure. * + * @param CarbonInterval $ci An interval to traverse by + * @param Closure $callback * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference * - * @return float + * @return int */ - public function floatDiffInYears($date = null, $absolute = true) + public function diffFiltered(CarbonInterval $ci, Closure $callback, $date = null, bool $absolute = false): int { $start = $this; $end = $this->resolveCarbon($date); - $ascending = ($start <= $end); - $sign = $absolute || $ascending ? 1 : -1; - if (!$ascending) { - [$start, $end] = [$end, $start]; - } - $yearsDiff = $start->diffInYears($end); - /** @var Carbon|CarbonImmutable $floorEnd */ - $floorEnd = $start->avoidMutation()->addYears($yearsDiff); + $inverse = false; - if ($floorEnd >= $end) { - return $sign * $yearsDiff; + if ($end < $start) { + $start = $end; + $end = $this; + $inverse = true; } - /** @var Carbon|CarbonImmutable $startOfYearAfterFloorEnd */ - $startOfYearAfterFloorEnd = $floorEnd->avoidMutation()->addYear()->startOfYear(); - - if ($startOfYearAfterFloorEnd > $end) { - return $sign * ($yearsDiff + $floorEnd->floatDiffInDays($end) / $floorEnd->daysInYear); - } + $options = CarbonPeriod::EXCLUDE_END_DATE | ($this->isMutable() ? 0 : CarbonPeriod::IMMUTABLE); + $diff = $ci->toPeriod($start, $end, $options)->filter($callback)->count(); - return $sign * ($yearsDiff + $floorEnd->floatDiffInDays($startOfYearAfterFloorEnd) / $floorEnd->daysInYear + $startOfYearAfterFloorEnd->floatDiffInDays($end) / $end->daysInYear); + return $inverse && !$absolute ? -$diff : $diff; } /** - * Get the difference in seconds as float (microsecond-precision) using timestamps. + * Get the difference in weekdays. * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference * - * @return float + * @return int */ - public function floatDiffInRealSeconds($date = null, $absolute = true) + public function diffInWeekdays($date = null, bool $absolute = false): int { - return $this->diffInRealMicroseconds($date, $absolute) / static::MICROSECONDS_PER_SECOND; + return $this->diffInDaysFiltered( + static fn (CarbonInterface $date) => $date->isWeekday(), + $this->resolveCarbon($date)->avoidMutation()->modify($this->format('H:i:s.u')), + $absolute, + ); } /** - * Get the difference in minutes as float (microsecond-precision) using timestamps. + * Get the difference in weekend days using a filter. * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference * - * @return float + * @return int */ - public function floatDiffInRealMinutes($date = null, $absolute = true) + public function diffInWeekendDays($date = null, bool $absolute = false): int { - return $this->floatDiffInRealSeconds($date, $absolute) / static::SECONDS_PER_MINUTE; + return $this->diffInDaysFiltered( + static fn (CarbonInterface $date) => $date->isWeekend(), + $this->resolveCarbon($date)->avoidMutation()->modify($this->format('H:i:s.u')), + $absolute, + ); } /** - * Get the difference in hours as float (microsecond-precision) using timestamps. + * Get the difference in hours. * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference * * @return float */ - public function floatDiffInRealHours($date = null, $absolute = true) + public function diffInHours($date = null, bool $absolute = false): float { - return $this->floatDiffInRealMinutes($date, $absolute) / static::MINUTES_PER_HOUR; + return $this->diffInMinutes($date, $absolute) / static::MINUTES_PER_HOUR; } /** - * Get the difference in days as float (microsecond-precision). + * Get the difference in minutes. * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference * * @return float */ - public function floatDiffInRealDays($date = null, $absolute = true) + public function diffInMinutes($date = null, bool $absolute = false): float { - $date = $this->resolveUTC($date); - $utc = $this->avoidMutation()->utc(); - $hoursDiff = $utc->floatDiffInRealHours($date, $absolute); - - return ($hoursDiff < 0 ? -1 : 1) * $utc->diffInDays($date) + fmod($hoursDiff, static::HOURS_PER_DAY) / static::HOURS_PER_DAY; + return $this->diffInSeconds($date, $absolute) / static::SECONDS_PER_MINUTE; } /** - * Get the difference in weeks as float (microsecond-precision). + * Get the difference in seconds. * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference * * @return float */ - public function floatDiffInRealWeeks($date = null, $absolute = true) + public function diffInSeconds($date = null, bool $absolute = false): float { - return $this->floatDiffInRealDays($date, $absolute) / static::DAYS_PER_WEEK; + return $this->diffInMilliseconds($date, $absolute) / static::MILLISECONDS_PER_SECOND; } /** - * Get the difference in months as float (microsecond-precision) using timestamps. + * Get the difference in microseconds. * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference * * @return float */ - public function floatDiffInRealMonths($date = null, $absolute = true) + public function diffInMicroseconds($date = null, bool $absolute = false): float { - $start = $this; - $end = $this->resolveCarbon($date); - $ascending = ($start <= $end); - $sign = $absolute || $ascending ? 1 : -1; - if (!$ascending) { - [$start, $end] = [$end, $start]; - } - $monthsDiff = $start->diffInMonths($end); - /** @var Carbon|CarbonImmutable $floorEnd */ - $floorEnd = $start->avoidMutation()->addMonths($monthsDiff); - - if ($floorEnd >= $end) { - return $sign * $monthsDiff; - } - - /** @var Carbon|CarbonImmutable $startOfMonthAfterFloorEnd */ - $startOfMonthAfterFloorEnd = $floorEnd->avoidMutation()->addMonth()->startOfMonth(); - - if ($startOfMonthAfterFloorEnd > $end) { - return $sign * ($monthsDiff + $floorEnd->floatDiffInRealDays($end) / $floorEnd->daysInMonth); - } + /** @var CarbonInterface $date */ + $date = $this->resolveCarbon($date); + $value = ($date->timestamp - $this->timestamp) * static::MICROSECONDS_PER_SECOND + + $date->micro - $this->micro; - return $sign * ($monthsDiff + $floorEnd->floatDiffInRealDays($startOfMonthAfterFloorEnd) / $floorEnd->daysInMonth + $startOfMonthAfterFloorEnd->floatDiffInRealDays($end) / $end->daysInMonth); + return $absolute ? abs($value) : $value; } /** - * Get the difference in year as float (microsecond-precision) using timestamps. + * Get the difference in milliseconds. * * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date * @param bool $absolute Get the absolute of the difference * * @return float */ - public function floatDiffInRealYears($date = null, $absolute = true) + public function diffInMilliseconds($date = null, bool $absolute = false): float { - $start = $this; - $end = $this->resolveCarbon($date); - $ascending = ($start <= $end); - $sign = $absolute || $ascending ? 1 : -1; - if (!$ascending) { - [$start, $end] = [$end, $start]; - } - $yearsDiff = $start->diffInYears($end); - /** @var Carbon|CarbonImmutable $floorEnd */ - $floorEnd = $start->avoidMutation()->addYears($yearsDiff); - - if ($floorEnd >= $end) { - return $sign * $yearsDiff; - } - - /** @var Carbon|CarbonImmutable $startOfYearAfterFloorEnd */ - $startOfYearAfterFloorEnd = $floorEnd->avoidMutation()->addYear()->startOfYear(); - - if ($startOfYearAfterFloorEnd > $end) { - return $sign * ($yearsDiff + $floorEnd->floatDiffInRealDays($end) / $floorEnd->daysInYear); - } - - return $sign * ($yearsDiff + $floorEnd->floatDiffInRealDays($startOfYearAfterFloorEnd) / $floorEnd->daysInYear + $startOfYearAfterFloorEnd->floatDiffInRealDays($end) / $end->daysInYear); + return $this->diffInMicroseconds($date, $absolute) / static::MICROSECONDS_PER_MILLISECOND; } /** * The number of seconds since midnight. * - * @return int + * @return float */ - public function secondsSinceMidnight() + public function secondsSinceMidnight(): float { - return $this->diffInSeconds($this->avoidMutation()->startOfDay()); + return $this->diffInSeconds($this->copy()->startOfDay(), true); } /** * The number of seconds until 23:59:59. * - * @return int + * @return float */ - public function secondsUntilEndOfDay() + public function secondsUntilEndOfDay(): float { - return $this->diffInSeconds($this->avoidMutation()->endOfDay()); + return $this->diffInSeconds($this->copy()->endOfDay(), true); } /** @@ -790,41 +470,44 @@ public function secondsUntilEndOfDay() * echo Carbon::tomorrow()->diffForHumans(Carbon::yesterday(), ['short' => true]) . "\n"; * ``` * - * @param Carbon|\DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below; - * if null passed, now will be used as comparison reference; - * if any other type, it will be converted to date and used as reference. - * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains: - * - 'syntax' entry (see below) - * - 'short' entry (see below) - * - 'parts' entry (see below) - * - 'options' entry (see below) - * - 'skip' entry, list of units to skip (array of strings or a single string, - * ` it can be the unit name (singular or plural) or its shortcut - * ` (y, m, w, d, h, min, s, ms, µs). - * - 'aUnit' entry, prefer "an hour" over "1 hour" if true - * - 'join' entry determines how to join multiple parts of the string - * ` - if $join is a string, it's used as a joiner glue - * ` - if $join is a callable/closure, it get the list of string and should return a string - * ` - if $join is an array, the first item will be the default glue, and the second item - * ` will be used instead of the glue for the last item - * ` - if $join is true, it will be guessed from the locale ('list' translation file entry) - * ` - if $join is missing, a space will be used as glue - * - 'other' entry (see above) - * - 'minimumUnit' entry determines the smallest unit of time to display can be long or - * ` short form of the units, e.g. 'hour' or 'h' (default value: s) - * if int passed, it add modifiers: - * Possible values: - * - CarbonInterface::DIFF_ABSOLUTE no modifiers - * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier - * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier - * Default value: CarbonInterface::DIFF_ABSOLUTE - * @param bool $short displays short format of time units - * @param int $parts maximum number of parts to display (default value: 1: single unit) - * @param int $options human diff options - * - * @return string - */ - public function diffForHumans($other = null, $syntax = null, $short = false, $parts = 1, $options = null) + * @param Carbon|DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below; + * if null passed, now will be used as comparison reference; + * if any other type, it will be converted to date and used as reference. + * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains: + * ⦿ 'syntax' entry (see below) + * ⦿ 'short' entry (see below) + * ⦿ 'parts' entry (see below) + * ⦿ 'options' entry (see below) + * ⦿ 'skip' entry, list of units to skip (array of strings or a single string, + * ` it can be the unit name (singular or plural) or its shortcut + * ` (y, m, w, d, h, min, s, ms, µs). + * ⦿ 'aUnit' entry, prefer "an hour" over "1 hour" if true + * ⦿ 'altNumbers' entry, use alternative numbers if available + * ` (from the current language if true is passed, from the given language(s) + * ` if array or string is passed) + * ⦿ 'join' entry determines how to join multiple parts of the string + * ` - if $join is a string, it's used as a joiner glue + * ` - if $join is a callable/closure, it get the list of string and should return a string + * ` - if $join is an array, the first item will be the default glue, and the second item + * ` will be used instead of the glue for the last item + * ` - if $join is true, it will be guessed from the locale ('list' translation file entry) + * ` - if $join is missing, a space will be used as glue + * ⦿ 'other' entry (see above) + * ⦿ 'minimumUnit' entry determines the smallest unit of time to display can be long or + * ` short form of the units, e.g. 'hour' or 'h' (default value: s) + * ⦿ 'locale' language in which the diff should be output (has no effect if 'translator' key is set) + * ⦿ 'translator' a custom translator to use to translator the output. + * if int passed, it adds modifiers: + * Possible values: + * - CarbonInterface::DIFF_ABSOLUTE no modifiers + * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier + * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier + * Default value: CarbonInterface::DIFF_ABSOLUTE + * @param bool $short displays short format of time units + * @param int $parts maximum number of parts to display (default value: 1: single unit) + * @param int $options human diff options + */ + public function diffForHumans($other = null, $syntax = null, $short = false, $parts = 1, $options = null): string { /* @var CarbonInterface $this */ if (\is_array($other)) { @@ -834,19 +517,22 @@ public function diffForHumans($other = null, $syntax = null, $short = false, $pa } $intSyntax = &$syntax; + if (\is_array($syntax)) { $syntax['syntax'] = $syntax['syntax'] ?? null; $intSyntax = &$syntax['syntax']; } + $intSyntax = (int) ($intSyntax ?? static::DIFF_RELATIVE_AUTO); $intSyntax = $intSyntax === static::DIFF_RELATIVE_AUTO && $other === null ? static::DIFF_RELATIVE_TO_NOW : $intSyntax; $parts = min(7, max(1, (int) $parts)); $skip = \is_array($syntax) ? ($syntax['skip'] ?? []) : []; + $options ??= $this->localHumanDiffOptions ?? $this->transmitFactory( + static fn () => static::getHumanDiffOptions(), + ); - return $this->diffAsCarbonInterval($other, false, (array) $skip) - ->setLocalTranslator($this->getLocalTranslator()) - ->forHumans($syntax, (bool) $short, $parts, $options ?? $this->localHumanDiffOptions ?? static::getHumanDiffOptions()); + return $this->diff($other, skip: (array) $skip)->forHumans($syntax, (bool) $short, $parts, $options); } /** @@ -1107,22 +793,21 @@ public function ago($syntax = null, $short = false, $parts = 1, $options = null) } /** - * Get the difference in a human readable format in the current locale from current instance to an other + * Get the difference in a human-readable format in the current locale from current instance to another * instance given (or now if null given). * * @return string */ - public function timespan($other = null, $timezone = null) + public function timespan($other = null, $timezone = null): string { - if (!$other instanceof DateTimeInterface) { - $other = static::parse($other, $timezone); + if (\is_string($other)) { + $other = $this->transmitFactory(static fn () => static::parse($other, $timezone)); } return $this->diffForHumans($other, [ 'join' => ', ', 'syntax' => CarbonInterface::DIFF_ABSOLUTE, - 'options' => CarbonInterface::NO_ZERO_DIFF, - 'parts' => -1, + 'parts' => INF, ]); } @@ -1144,12 +829,12 @@ public function calendar($referenceTime = null, array $formats = []) /** @var CarbonInterface $other */ $other = $this->resolveCarbon($referenceTime)->avoidMutation()->setTimezone($this->getTimezone())->startOfDay(); $diff = $other->diffInDays($current, false); - $format = $diff < -6 ? 'sameElse' : ( + $format = $diff <= -static::DAYS_PER_WEEK ? 'sameElse' : ( $diff < -1 ? 'lastWeek' : ( $diff < 0 ? 'lastDay' : ( $diff < 1 ? 'sameDay' : ( $diff < 2 ? 'nextDay' : ( - $diff < 7 ? 'nextWeek' : 'sameElse' + $diff < static::DAYS_PER_WEEK ? 'nextWeek' : 'sameElse' ) ) ) @@ -1165,18 +850,6 @@ public function calendar($referenceTime = null, array $formats = []) private function getIntervalDayDiff(DateInterval $interval): int { - $daysDiff = (int) $interval->format('%a'); - $sign = $interval->format('%r') === '-' ? -1 : 1; - - if (\is_int($interval->days) && - $interval->y === 0 && - $interval->m === 0 && - version_compare(PHP_VERSION, '8.1.0-dev', '<') && - abs($interval->d - $daysDiff) === 1 - ) { - $daysDiff = abs($interval->d); // @codeCoverageIgnore - } - - return $daysDiff * $sign; + return (int) $interval->format('%r%a'); } } diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/IntervalRounding.php b/vendor/nesbot/carbon/src/Carbon/Traits/IntervalRounding.php index f069c280d..e27c7bab7 100644 --- a/vendor/nesbot/carbon/src/Carbon/Traits/IntervalRounding.php +++ b/vendor/nesbot/carbon/src/Carbon/Traits/IntervalRounding.php @@ -1,5 +1,7 @@ forHumans(['locale' => 'en']); } if (\is_string($precision) && preg_match('/^\s*(?\d+)?\s*(?\w+)(?\W.*)?$/', $precision, $match)) { diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/IntervalStep.php b/vendor/nesbot/carbon/src/Carbon/Traits/IntervalStep.php index 82d7c3264..2eaf9845d 100644 --- a/vendor/nesbot/carbon/src/Carbon/Traits/IntervalStep.php +++ b/vendor/nesbot/carbon/src/Carbon/Traits/IntervalStep.php @@ -1,5 +1,7 @@ resolveCarbon($dateTime); if ($this->step) { - return $carbonDate->setDateTimeFrom(($this->step)($carbonDate->avoidMutation(), $negated)); + $carbonDate = Callback::parameter($this->step, $carbonDate->avoidMutation()); + + return $carbonDate->modify(($this->step)($carbonDate, $negated)->format('Y-m-d H:i:s.u e O')); } if ($negated) { @@ -77,12 +82,8 @@ public function convertDate(DateTimeInterface $dateTime, bool $negated = false): /** * Convert DateTimeImmutable instance to CarbonImmutable instance and DateTime instance to Carbon instance. - * - * @param DateTimeInterface $dateTime - * - * @return Carbon|CarbonImmutable */ - private function resolveCarbon(DateTimeInterface $dateTime) + private function resolveCarbon(DateTimeInterface $dateTime): Carbon|CarbonImmutable { if ($dateTime instanceof DateTimeImmutable) { return CarbonImmutable::instance($dateTime); diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/LocalFactory.php b/vendor/nesbot/carbon/src/Carbon/Traits/LocalFactory.php new file mode 100644 index 000000000..a03985498 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/LocalFactory.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +use Carbon\Factory; +use Carbon\FactoryImmutable; +use Carbon\WrapperClock; +use Closure; + +/** + * Remember the factory that was the current at the creation of the object. + */ +trait LocalFactory +{ + /** + * The clock that generated the current instance (or FactoryImmutable::getDefaultInstance() if none) + */ + private ?WrapperClock $clock = null; + + public function getClock(): ?WrapperClock + { + return $this->clock; + } + + private function initLocalFactory(): void + { + $this->clock = FactoryImmutable::getCurrentClock(); + } + + /** + * Trigger the given action using the local factory of the object, so it will be transmitted + * to any object also using this trait and calling initLocalFactory() in its constructor. + * + * @template T + * + * @param Closure(): T $action + * + * @return T + */ + private function transmitFactory(Closure $action): mixed + { + $previousClock = FactoryImmutable::getCurrentClock(); + FactoryImmutable::setCurrentClock($this->clock); + + try { + return $action(); + } finally { + FactoryImmutable::setCurrentClock($previousClock); + } + } + + private function getFactory(): Factory + { + return $this->getClock()?->getFactory() ?? FactoryImmutable::getDefaultInstance(); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Localization.php b/vendor/nesbot/carbon/src/Carbon/Traits/Localization.php index 46aff113e..8da7a17a3 100644 --- a/vendor/nesbot/carbon/src/Carbon/Traits/Localization.php +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Localization.php @@ -1,5 +1,7 @@ settings() method. - * @see settings - * - * @param int $humanDiffOptions - */ - public static function setHumanDiffOptions($humanDiffOptions) - { - static::$humanDiffOptions = $humanDiffOptions; - } - - /** - * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use the ->settings() method. - * @see settings - * - * @param int $humanDiffOption - */ - public static function enableHumanDiffOption($humanDiffOption) - { - static::$humanDiffOptions = static::getHumanDiffOptions() | $humanDiffOption; - } - - /** - * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use the ->settings() method. - * @see settings - * - * @param int $humanDiffOption - */ - public static function disableHumanDiffOption($humanDiffOption) - { - static::$humanDiffOptions = static::getHumanDiffOptions() & ~$humanDiffOption; - } - - /** - * Return default humanDiff() options (merged flags as integer). - * - * @return int - */ - public static function getHumanDiffOptions() - { - return static::$humanDiffOptions; - } - - /** - * Get the default translator instance in use. - * - * @return \Symfony\Component\Translation\TranslatorInterface - */ - public static function getTranslator() - { - return static::translator(); - } - - /** - * Set the default translator instance to use. - * - * @param \Symfony\Component\Translation\TranslatorInterface $translator - * - * @return void - */ - public static function setTranslator(TranslatorInterface $translator) - { - static::$translator = $translator; - } + protected ?TranslatorInterface $localTranslator = null; /** * Return true if the current instance has its own translator. - * - * @return bool */ - public function hasLocalTranslator() + public function hasLocalTranslator(): bool { return isset($this->localTranslator); } /** * Get the translator of the current instance or the default if none set. - * - * @return \Symfony\Component\Translation\TranslatorInterface */ - public function getLocalTranslator() + public function getLocalTranslator(): TranslatorInterface { - return $this->localTranslator ?: static::translator(); + return $this->localTranslator ?? $this->transmitFactory(static fn () => static::getTranslator()); } /** * Set the translator for the current instance. - * - * @param \Symfony\Component\Translation\TranslatorInterface $translator - * - * @return $this */ - public function setLocalTranslator(TranslatorInterface $translator) + public function setLocalTranslator(TranslatorInterface $translator): self { $this->localTranslator = $translator; @@ -167,19 +67,19 @@ public function setLocalTranslator(TranslatorInterface $translator) /** * Returns raw translation message for a given key. * - * @param \Symfony\Component\Translation\TranslatorInterface $translator the translator to use - * @param string $key key to find - * @param string|null $locale current locale used if null - * @param string|null $default default value if translation returns the key + * @param TranslatorInterface|null $translator the translator to use + * @param string $key key to find + * @param string|null $locale current locale used if null + * @param string|null $default default value if translation returns the key * - * @return string + * @return string|Closure|null */ public static function getTranslationMessageWith($translator, string $key, ?string $locale = null, ?string $default = null) { if (!($translator instanceof TranslatorBagInterface && $translator instanceof TranslatorInterface)) { throw new InvalidTypeException( 'Translator does not implement '.TranslatorInterface::class.' and '.TranslatorBagInterface::class.'. '. - (\is_object($translator) ? \get_class($translator) : \gettype($translator)).' has been given.' + (\is_object($translator) ? \get_class($translator) : \gettype($translator)).' has been given.', ); } @@ -195,25 +95,25 @@ public static function getTranslationMessageWith($translator, string $key, ?stri /** * Returns raw translation message for a given key. * - * @param string $key key to find - * @param string|null $locale current locale used if null - * @param string|null $default default value if translation returns the key - * @param \Symfony\Component\Translation\TranslatorInterface $translator an optional translator to use + * @param string $key key to find + * @param string|null $locale current locale used if null + * @param string|null $default default value if translation returns the key + * @param TranslatorInterface $translator an optional translator to use * * @return string */ public function getTranslationMessage(string $key, ?string $locale = null, ?string $default = null, $translator = null) { - return static::getTranslationMessageWith($translator ?: $this->getLocalTranslator(), $key, $locale, $default); + return static::getTranslationMessageWith($translator ?? $this->getLocalTranslator(), $key, $locale, $default); } /** * Translate using translation string or callback available. * - * @param \Symfony\Component\Translation\TranslatorInterface $translator - * @param string $key - * @param array $parameters - * @param null $number + * @param TranslatorInterface $translator an optional translator to use + * @param string $key key to find + * @param array $parameters replacement parameters + * @param int|float|null $number number if plural * * @return string */ @@ -231,32 +131,31 @@ public static function translateWith(TranslatorInterface $translator, string $ke $parameters[':count'] = $parameters['%count%']; } - // @codeCoverageIgnoreStart - $choice = $translator instanceof ContractsTranslatorInterface - ? $translator->trans($key, $parameters) - : $translator->transChoice($key, $number, $parameters); - // @codeCoverageIgnoreEnd - - return (string) $choice; + return (string) $translator->trans($key, $parameters); } /** * Translate using translation string or callback available. * - * @param string $key - * @param array $parameters - * @param string|int|float|null $number - * @param \Symfony\Component\Translation\TranslatorInterface|null $translator - * @param bool $altNumbers + * @param string $key key to find + * @param array $parameters replacement parameters + * @param string|int|float|null $number number if plural + * @param TranslatorInterface|null $translator an optional translator to use + * @param bool $altNumbers pass true to use alternative numbers * * @return string */ - public function translate(string $key, array $parameters = [], $number = null, ?TranslatorInterface $translator = null, bool $altNumbers = false): string - { - $translation = static::translateWith($translator ?: $this->getLocalTranslator(), $key, $parameters, $number); + public function translate( + string $key, + array $parameters = [], + string|int|float|null $number = null, + ?TranslatorInterface $translator = null, + bool $altNumbers = false, + ): string { + $translation = static::translateWith($translator ?? $this->getLocalTranslator(), $key, $parameters, $number); if ($number !== null && $altNumbers) { - return str_replace($number, $this->translateNumber($number), $translation); + return str_replace((string) $number, $this->translateNumber((int) $number), $translation); } return $translation; @@ -328,11 +227,15 @@ public function translateNumber(int $number): string * * @return string */ - public static function translateTimeString($timeString, $from = null, $to = null, $mode = CarbonInterface::TRANSLATE_ALL) - { + public static function translateTimeString( + string $timeString, + ?string $from = null, + ?string $to = null, + int $mode = CarbonInterface::TRANSLATE_ALL, + ): string { // Fallback source and destination locales $from = $from ?: static::getLocale(); - $to = $to ?: 'en'; + $to = $to ?: CarbonInterface::DEFAULT_LOCALE; if ($from === $to) { return $timeString; @@ -379,10 +282,10 @@ public static function translateTimeString($timeString, $from = null, $to = null } $$translationKey = array_merge( - $mode & CarbonInterface::TRANSLATE_MONTHS ? static::getTranslationArray($months, 12, $timeString) : [], - $mode & CarbonInterface::TRANSLATE_MONTHS ? static::getTranslationArray($messages['months_short'] ?? [], 12, $timeString) : [], - $mode & CarbonInterface::TRANSLATE_DAYS ? static::getTranslationArray($weekdays, 7, $timeString) : [], - $mode & CarbonInterface::TRANSLATE_DAYS ? static::getTranslationArray($messages['weekdays_short'] ?? [], 7, $timeString) : [], + $mode & CarbonInterface::TRANSLATE_MONTHS ? static::getTranslationArray($months, static::MONTHS_PER_YEAR, $timeString) : [], + $mode & CarbonInterface::TRANSLATE_MONTHS ? static::getTranslationArray($messages['months_short'] ?? [], static::MONTHS_PER_YEAR, $timeString) : [], + $mode & CarbonInterface::TRANSLATE_DAYS ? static::getTranslationArray($weekdays, static::DAYS_PER_WEEK, $timeString) : [], + $mode & CarbonInterface::TRANSLATE_DAYS ? static::getTranslationArray($messages['weekdays_short'] ?? [], static::DAYS_PER_WEEK, $timeString) : [], $mode & CarbonInterface::TRANSLATE_DIFF ? static::translateWordsByKeys([ 'diff_now', 'diff_today', @@ -402,11 +305,11 @@ public static function translateTimeString($timeString, $from = null, $to = null ], $messages, $key) : [], $mode & CarbonInterface::TRANSLATE_MERIDIEM ? array_map(function ($hour) use ($meridiem) { if (\is_array($meridiem)) { - return $meridiem[$hour < 12 ? 0 : 1]; + return $meridiem[$hour < static::HOURS_PER_DAY / 2 ? 0 : 1]; } return $meridiem($hour, 0, false); - }, range(0, 23)) : [] + }, range(0, 23)) : [], ); } @@ -424,14 +327,14 @@ public static function translateTimeString($timeString, $from = null, $to = null } /** - * Translate a time string from the current locale (`$date->locale()`) to an other. + * Translate a time string from the current locale (`$date->locale()`) to another one. * * @param string $timeString time string to translate * @param string|null $to output locale of the result returned ("en" by default) * * @return string */ - public function translateTimeStringTo($timeString, $to = null) + public function translateTimeStringTo(string $timeString, ?string $to = null): string { return static::translateTimeString($timeString, $this->getTranslatorLocale(), $to); } @@ -444,7 +347,7 @@ public function translateTimeStringTo($timeString, $to = null) * * @return $this|string */ - public function locale(string $locale = null, ...$fallbackLocales) + public function locale(?string $locale = null, string ...$fallbackLocales): static|string { if ($locale === null) { return $this->getTranslatorLocale(); @@ -476,22 +379,20 @@ public function locale(string $locale = null, ...$fallbackLocales) * * @return string */ - public static function getLocale() + public static function getLocale(): string { return static::getLocaleAwareTranslator()->getLocale(); } /** * Set the current translator locale and indicate if the source locale file exists. - * Pass 'auto' as locale to use closest language from the current LC_TIME locale. + * Pass 'auto' as locale to use the closest language to the current LC_TIME locale. * * @param string $locale locale ex. en - * - * @return bool */ - public static function setLocale($locale) + public static function setLocale(string $locale): void { - return static::getLocaleAwareTranslator()->setLocale($locale) !== false; + static::getLocaleAwareTranslator()->setLocale($locale); } /** @@ -501,7 +402,7 @@ public static function setLocale($locale) * * @param string $locale */ - public static function setFallbackLocale($locale) + public static function setFallbackLocale(string $locale): void { $translator = static::getTranslator(); @@ -513,7 +414,7 @@ public static function setFallbackLocale($locale) $translator->setMessages($preferredLocale, array_replace_recursive( $translator->getMessages()[$locale] ?? [], Translator::get($locale)->getMessages()[$locale] ?? [], - $translator->getMessages($preferredLocale) + $translator->getMessages($preferredLocale), )); } } @@ -523,10 +424,8 @@ public static function setFallbackLocale($locale) * Get the fallback locale. * * @see https://symfony.com/doc/current/components/translation.html#fallback-locales - * - * @return string|null */ - public static function getFallbackLocale() + public static function getFallbackLocale(): ?string { $translator = static::getTranslator(); @@ -546,10 +445,17 @@ public static function getFallbackLocale() * * @return mixed */ - public static function executeWithLocale($locale, $func) + public static function executeWithLocale(string $locale, callable $func): mixed { $currentLocale = static::getLocale(); - $result = $func(static::setLocale($locale) ? static::getLocale() : false, static::translator()); + static::setLocale($locale); + $newLocale = static::getLocale(); + $result = $func( + $newLocale === 'en' && strtolower(substr((string) $locale, 0, 2)) !== 'en' + ? false + : $newLocale, + static::getTranslator(), + ); static::setLocale($currentLocale); return $result; @@ -563,7 +469,7 @@ public static function executeWithLocale($locale, $func) * * @return bool */ - public static function localeHasShortUnits($locale) + public static function localeHasShortUnits(string $locale): bool { return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) { return ($newLocale && (($y = static::translateWith($translator, 'y')) !== 'y' && $y !== static::translateWith($translator, 'year'))) || ( @@ -584,7 +490,7 @@ public static function localeHasShortUnits($locale) * * @return bool */ - public static function localeHasDiffSyntax($locale) + public static function localeHasDiffSyntax(string $locale): bool { return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) { if (!$newLocale) { @@ -615,7 +521,7 @@ public static function localeHasDiffSyntax($locale) * * @return bool */ - public static function localeHasDiffOneDayWords($locale) + public static function localeHasDiffOneDayWords(string $locale): bool { return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) { return $newLocale && @@ -633,7 +539,7 @@ public static function localeHasDiffOneDayWords($locale) * * @return bool */ - public static function localeHasDiffTwoDayWords($locale) + public static function localeHasDiffTwoDayWords(string $locale): bool { return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) { return $newLocale && @@ -692,29 +598,11 @@ public static function getAvailableLocalesInfo() return $languages; } - /** - * Initialize the default translator instance if necessary. - * - * @return \Symfony\Component\Translation\TranslatorInterface - */ - protected static function translator() - { - if (static::$translator === null) { - static::$translator = Translator::get(); - } - - return static::$translator; - } - /** * Get the locale of a given translator. * * If null or omitted, current local translator is used. * If no local translator is in use, current global translator is used. - * - * @param null $translator - * - * @return string|null */ protected function getTranslatorLocale($translator = null): ?string { @@ -724,7 +612,7 @@ protected function getTranslatorLocale($translator = null): ?string $translator = static::getLocaleAwareTranslator($translator); - return $translator ? $translator->getLocale() : null; + return $translator?->getLocale(); } /** @@ -737,7 +625,7 @@ protected function getTranslatorLocale($translator = null): ?string protected static function getLocaleAwareTranslator($translator = null) { if (\func_num_args() === 0) { - $translator = static::translator(); + $translator = static::getTranslator(); } if ($translator && !($translator instanceof LocaleAwareInterface || method_exists($translator, 'getLocale'))) { @@ -756,8 +644,8 @@ protected static function getLocaleAwareTranslator($translator = null) private static function getFromCatalogue($translator, $catalogue, string $id, string $domain = 'messages') { return $translator instanceof TranslatorStrongTypeInterface - ? $translator->getFromCatalogue($catalogue, $id, $domain) // @codeCoverageIgnore - : $catalogue->get($id, $domain); + ? $translator->getFromCatalogue($catalogue, $id, $domain) + : $catalogue->get($id, $domain); // @codeCoverageIgnore } /** @@ -771,7 +659,11 @@ private static function cleanWordFromTranslationString($word) { $word = str_replace([':count', '%count', ':time'], '', $word); $word = strtr($word, ['’' => "'"]); - $word = preg_replace('/({\d+(,(\d+|Inf))?}|[\[\]]\d+(,(\d+|Inf))?[\[\]])/', '', $word); + $word = preg_replace( + '/\{(?:-?\d+(?:\.\d+)?|-?Inf)(?:,(?:-?\d+|-?Inf))?}|[\[\]](?:-?\d+(?:\.\d+)?|-?Inf)(?:,(?:-?\d+|-?Inf))?[\[\]]/', + '', + $word, + ); return trim($word); } @@ -800,7 +692,7 @@ private static function translateWordsByKeys($keys, $messages, $key): array return $key === 'to' ? self::cleanWordFromTranslationString(end($parts)) - : '(?:'.implode('|', array_map([static::class, 'cleanWordFromTranslationString'], $parts)).')'; + : '(?:'.implode('|', array_map(static::cleanWordFromTranslationString(...), $parts)).')'; }, $keys); } diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Macro.php b/vendor/nesbot/carbon/src/Carbon/Traits/Macro.php index 92b6c9d86..978c1994b 100644 --- a/vendor/nesbot/carbon/src/Carbon/Traits/Macro.php +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Macro.php @@ -1,5 +1,7 @@ hours(11)->userFormat(); * ``` * - * @param string $name - * @param object|callable $macro - * - * @return void + * @param-closure-this static $macro */ - public static function macro($name, $macro) + public static function macro(string $name, ?callable $macro): void { - static::$globalMacros[$name] = $macro; + FactoryImmutable::getDefaultInstance()->macro($name, $macro); } /** * Remove all macros and generic macros. */ - public static function resetMacros() + public static function resetMacros(): void { - static::$globalMacros = []; - static::$globalGenericMacros = []; + FactoryImmutable::getDefaultInstance()->resetMacros(); } /** * Register a custom macro. * - * @param object|callable $macro - * @param int $priority marco with higher priority is tried first + * @param callable $macro + * @param int $priority marco with higher priority is tried first * * @return void */ - public static function genericMacro($macro, $priority = 0) + public static function genericMacro(callable $macro, int $priority = 0): void { - if (!isset(static::$globalGenericMacros[$priority])) { - static::$globalGenericMacros[$priority] = []; - krsort(static::$globalGenericMacros, SORT_NUMERIC); - } - - static::$globalGenericMacros[$priority][] = $macro; + FactoryImmutable::getDefaultInstance()->genericMacro($macro, $priority); } /** @@ -93,44 +76,36 @@ public static function genericMacro($macro, $priority = 0) * * @return bool */ - public static function hasMacro($name) + public static function hasMacro(string $name): bool { - return isset(static::$globalMacros[$name]); + return FactoryImmutable::getInstance()->hasMacro($name); } /** * Get the raw callable macro registered globally for a given name. - * - * @param string $name - * - * @return callable|null */ - public static function getMacro($name) + public static function getMacro(string $name): ?callable { - return static::$globalMacros[$name] ?? null; + return FactoryImmutable::getInstance()->getMacro($name); } /** * Checks if macro is registered globally or locally. - * - * @param string $name - * - * @return bool */ - public function hasLocalMacro($name) + public function hasLocalMacro(string $name): bool { - return ($this->localMacros && isset($this->localMacros[$name])) || static::hasMacro($name); + return ($this->localMacros && isset($this->localMacros[$name])) || $this->transmitFactory( + static fn () => static::hasMacro($name), + ); } /** * Get the raw callable macro registered globally or locally for a given name. - * - * @param string $name - * - * @return callable|null */ - public function getLocalMacro($name) + public function getLocalMacro(string $name): ?callable { - return ($this->localMacros ?? [])[$name] ?? static::getMacro($name); + return ($this->localMacros ?? [])[$name] ?? $this->transmitFactory( + static fn () => static::getMacro($name), + ); } } diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/MagicParameter.php b/vendor/nesbot/carbon/src/Carbon/Traits/MagicParameter.php index 310a44d0c..d6595f186 100644 --- a/vendor/nesbot/carbon/src/Carbon/Traits/MagicParameter.php +++ b/vendor/nesbot/carbon/src/Carbon/Traits/MagicParameter.php @@ -1,5 +1,7 @@ getMethods( - ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED + ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED, ); foreach ($methods as $method) { @@ -89,16 +83,15 @@ private static function loadMixinClass($mixin) continue; } - $method->setAccessible(true); + $macro = $method->invoke($mixin); - static::macro($method->name, $method->invoke($mixin)); + if (\is_callable($macro)) { + static::macro($method->name, $macro); + } } } - /** - * @param string $trait - */ - private static function loadMixinTrait($trait) + private static function loadMixinTrait(string $trait): void { $context = eval(self::getAnonymousClassCodeForTrait($trait)); $className = \get_class($context); @@ -114,7 +107,7 @@ private static function loadMixinTrait($trait) try { // @ is required to handle error if not converted into exceptions $closure = @$closureBase->bindTo($context); - } catch (Throwable $throwable) { // @codeCoverageIgnore + } catch (Throwable) { // @codeCoverageIgnore $closure = $closureBase; // @codeCoverageIgnore } @@ -167,7 +160,7 @@ private static function loadMixinTrait($trait) } } - private static function getAnonymousClassCodeForTrait(string $trait) + private static function getAnonymousClassCodeForTrait(string $trait): string { return 'return new class() extends '.static::class.' {use '.$trait.';};'; } @@ -185,15 +178,8 @@ private static function getMixableMethods(self $context): Generator /** * Stack a Carbon context from inside calls of self::this() and execute a given action. - * - * @param static|null $context - * @param callable $callable - * - * @throws Throwable - * - * @return mixed */ - protected static function bindMacroContext($context, callable $callable) + protected static function bindMacroContext(?self $context, callable $callable): mixed { static::$macroContextStack[] = $context; @@ -206,20 +192,16 @@ protected static function bindMacroContext($context, callable $callable) /** * Return the current context from inside a macro callee or a null if static. - * - * @return static|null */ - protected static function context() + protected static function context(): ?static { return end(static::$macroContextStack) ?: null; } /** * Return the current context from inside a macro callee or a new one if static. - * - * @return static */ - protected static function this() + protected static function this(): static { return end(static::$macroContextStack) ?: new static(); } diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Modifiers.php b/vendor/nesbot/carbon/src/Carbon/Traits/Modifiers.php index 39343d8fa..7fc0f9f6c 100644 --- a/vendor/nesbot/carbon/src/Carbon/Traits/Modifiers.php +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Modifiers.php @@ -1,5 +1,7 @@ change( - 'next '.(\is_string($modifier) ? $modifier : static::$days[$modifier]) + 'next '.(\is_string($modifier) ? $modifier : static::$days[$modifier]), ); } @@ -157,7 +160,7 @@ public function previousWeekendDay() * * @param string|int|null $modifier * - * @return static|false + * @return static */ public function previous($modifier = null) { @@ -166,7 +169,7 @@ public function previous($modifier = null) } return $this->change( - 'last '.(\is_string($modifier) ? $modifier : static::$days[$modifier]) + 'last '.(\is_string($modifier) ? $modifier : static::$days[$modifier]), ); } @@ -341,7 +344,7 @@ public function nthOfYear($nth, $dayOfWeek) */ public function average($date = null) { - return $this->addRealMicroseconds((int) ($this->diffInRealMicroseconds($this->resolveCarbon($date), false) / 2)); + return $this->addRealMicroseconds((int) ($this->diffInMicroseconds($this->resolveCarbon($date), false) / 2)); } /** @@ -354,7 +357,7 @@ public function average($date = null) */ public function closest($date1, $date2) { - return $this->diffInRealMicroseconds($date1) < $this->diffInRealMicroseconds($date2) ? $date1 : $date2; + return $this->diffInMicroseconds($date1, true) < $this->diffInMicroseconds($date2, true) ? $date1 : $date2; } /** @@ -367,7 +370,7 @@ public function closest($date1, $date2) */ public function farthest($date1, $date2) { - return $this->diffInRealMicroseconds($date1) > $this->diffInRealMicroseconds($date2) ? $date1 : $date2; + return $this->diffInMicroseconds($date1, true) > $this->diffInMicroseconds($date2, true) ? $date1 : $date2; } /** @@ -431,12 +434,13 @@ public function maximum($date = null) * * @see https://php.net/manual/en/datetime.modify.php * - * @return static|false + * @return static */ #[ReturnTypeWillChange] public function modify($modify) { - return parent::modify((string) $modify); + return parent::modify((string) $modify) + ?: throw new InvalidFormatException('Could not modify with: '.var_export($modify, true)); } /** @@ -451,7 +455,7 @@ public function modify($modify) * * @param string $modifier * - * @return static|false + * @return static */ public function change($modifier) { diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Mutability.php b/vendor/nesbot/carbon/src/Carbon/Traits/Mutability.php index 561c867d0..9f45f5859 100644 --- a/vendor/nesbot/carbon/src/Carbon/Traits/Mutability.php +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Mutability.php @@ -1,5 +1,7 @@ - */ - protected static $regexFormats = [ - 'd' => '(3[01]|[12][0-9]|0[1-9])', - 'D' => '(Sun|Mon|Tue|Wed|Thu|Fri|Sat)', - 'j' => '([123][0-9]|[1-9])', - 'l' => '([a-zA-Z]{2,})', - 'N' => '([1-7])', - 'S' => '(st|nd|rd|th)', - 'w' => '([0-6])', - 'z' => '(36[0-5]|3[0-5][0-9]|[12][0-9]{2}|[1-9]?[0-9])', - 'W' => '(5[012]|[1-4][0-9]|0?[1-9])', - 'F' => '([a-zA-Z]{2,})', - 'm' => '(1[012]|0[1-9])', - 'M' => '([a-zA-Z]{3})', - 'n' => '(1[012]|[1-9])', - 't' => '(2[89]|3[01])', - 'L' => '(0|1)', - 'o' => '([1-9][0-9]{0,4})', - 'Y' => '([1-9]?[0-9]{4})', - 'y' => '([0-9]{2})', - 'a' => '(am|pm)', - 'A' => '(AM|PM)', - 'B' => '([0-9]{3})', - 'g' => '(1[012]|[1-9])', - 'G' => '(2[0-3]|1?[0-9])', - 'h' => '(1[012]|0[1-9])', - 'H' => '(2[0-3]|[01][0-9])', - 'i' => '([0-5][0-9])', - 's' => '([0-5][0-9])', - 'u' => '([0-9]{1,6})', - 'v' => '([0-9]{1,3})', - 'e' => '([a-zA-Z]{1,5})|([a-zA-Z]*\\/[a-zA-Z]*)', - 'I' => '(0|1)', - 'O' => '([+-](1[0123]|0[0-9])[0134][05])', - 'P' => '([+-](1[0123]|0[0-9]):[0134][05])', - 'p' => '(Z|[+-](1[0123]|0[0-9]):[0134][05])', - 'T' => '([a-zA-Z]{1,5})', - 'Z' => '(-?[1-5]?[0-9]{1,4})', - 'U' => '([0-9]*)', - - // The formats below are combinations of the above formats. - 'c' => '(([1-9]?[0-9]{4})-(1[012]|0[1-9])-(3[01]|[12][0-9]|0[1-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])[+-](1[012]|0[0-9]):([0134][05]))', // Y-m-dTH:i:sP - 'r' => '(([a-zA-Z]{3}), ([123][0-9]|0[1-9]) ([a-zA-Z]{3}) ([1-9]?[0-9]{4}) (2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]) [+-](1[012]|0[0-9])([0134][05]))', // D, d M Y H:i:s O - ]; - - /** - * Format modifiers (such as available in createFromFormat) regex patterns. - * - * @var array - */ - protected static $regexFormatModifiers = [ - '*' => '.+', - ' ' => '[ ]', - '#' => '[;:\\/.,()-]', - '?' => '([^a]|[a])', - '!' => '', - '|' => '', - '+' => '', - ]; - - /** - * Indicates if months should be calculated with overflow. - * Global setting. - * - * @var bool - */ - protected static $monthsOverflow = true; - - /** - * Indicates if years should be calculated with overflow. - * Global setting. - * - * @var bool - */ - protected static $yearsOverflow = true; - - /** - * Indicates if the strict mode is in use. - * Global setting. - * - * @var bool - */ - protected static $strictModeEnabled = true; - - /** - * Function to call instead of format. - * - * @var string|callable|null - */ - protected static $formatFunction; - - /** - * Function to call instead of createFromFormat. - * - * @var string|callable|null - */ - protected static $createFromFormatFunction; - - /** - * Function to call instead of parse. - * - * @var string|callable|null - */ - protected static $parseFunction; - /** * Indicates if months should be calculated with overflow. * Specific setting. - * - * @var bool|null */ - protected $localMonthsOverflow; + protected ?bool $localMonthsOverflow = null; /** * Indicates if years should be calculated with overflow. * Specific setting. - * - * @var bool|null */ - protected $localYearsOverflow; + protected ?bool $localYearsOverflow = null; /** * Indicates if the strict mode is in use. * Specific setting. - * - * @var bool|null */ - protected $localStrictModeEnabled; + protected ?bool $localStrictModeEnabled = null; /** * Options for diffForHumans and forHumans methods. - * - * @var bool|null */ - protected $localHumanDiffOptions; + protected ?int $localHumanDiffOptions = null; /** * Format to use on string cast. * - * @var string|null + * @var string|callable|null */ - protected $localToStringFormat; + protected $localToStringFormat = null; /** * Format to use on JSON serialization. * - * @var string|null + * @var string|callable|null */ - protected $localSerializer; + protected $localSerializer = null; /** * Instance-specific macros. - * - * @var array|null */ - protected $localMacros; + protected ?array $localMacros = null; /** * Instance-specific generic macros. - * - * @var array|null */ - protected $localGenericMacros; + protected ?array $localGenericMacros = null; /** * Function to call instead of format. * * @var string|callable|null */ - protected $localFormatFunction; - - /** - * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use the ->settings() method. - * @see settings - * - * Enable the strict mode (or disable with passing false). - * - * @param bool $strictModeEnabled - */ - public static function useStrictMode($strictModeEnabled = true) - { - static::$strictModeEnabled = $strictModeEnabled; - } - - /** - * Returns true if the strict mode is globally in use, false else. - * (It can be overridden in specific instances.) - * - * @return bool - */ - public static function isStrictModeEnabled() - { - return static::$strictModeEnabled; - } - - /** - * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use the ->settings() method. - * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants - * are available for quarters, years, decade, centuries, millennia (singular and plural forms). - * @see settings - * - * Indicates if months should be calculated with overflow. - * - * @param bool $monthsOverflow - * - * @return void - */ - public static function useMonthsOverflow($monthsOverflow = true) - { - static::$monthsOverflow = $monthsOverflow; - } - - /** - * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use the ->settings() method. - * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants - * are available for quarters, years, decade, centuries, millennia (singular and plural forms). - * @see settings - * - * Reset the month overflow behavior. - * - * @return void - */ - public static function resetMonthsOverflow() - { - static::$monthsOverflow = true; - } - - /** - * Get the month overflow global behavior (can be overridden in specific instances). - * - * @return bool - */ - public static function shouldOverflowMonths() - { - return static::$monthsOverflow; - } - - /** - * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use the ->settings() method. - * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants - * are available for quarters, years, decade, centuries, millennia (singular and plural forms). - * @see settings - * - * Indicates if years should be calculated with overflow. - * - * @param bool $yearsOverflow - * - * @return void - */ - public static function useYearsOverflow($yearsOverflow = true) - { - static::$yearsOverflow = $yearsOverflow; - } - - /** - * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. - * You should rather use the ->settings() method. - * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants - * are available for quarters, years, decade, centuries, millennia (singular and plural forms). - * @see settings - * - * Reset the month overflow behavior. - * - * @return void - */ - public static function resetYearsOverflow() - { - static::$yearsOverflow = true; - } - - /** - * Get the month overflow global behavior (can be overridden in specific instances). - * - * @return bool - */ - public static function shouldOverflowYears() - { - return static::$yearsOverflow; - } + protected $localFormatFunction = null; /** * Set specific options. @@ -364,7 +102,7 @@ public static function shouldOverflowYears() * * @return $this|static */ - public function settings(array $settings) + public function settings(array $settings): static { $this->localStrictModeEnabled = $settings['strictMode'] ?? null; $this->localMonthsOverflow = $settings['monthOverflow'] ?? null; @@ -384,6 +122,8 @@ public function settings(array $settings) } $this->locale(...$locales); + } elseif (isset($settings['translator']) && property_exists($this, 'localTranslator')) { + $this->localTranslator = $settings['translator']; } if (isset($settings['innerTimezone'])) { @@ -399,10 +139,8 @@ public function settings(array $settings) /** * Returns current local settings. - * - * @return array */ - public function getSettings() + public function getSettings(): array { $settings = []; $map = [ @@ -432,16 +170,14 @@ public function getSettings() /** * Show truthy properties on var_dump(). - * - * @return array */ - public function __debugInfo() + public function __debugInfo(): array { $infos = array_filter(get_object_vars($this), static function ($var) { return $var; }); - foreach (['dumpProperties', 'constructedObjectId', 'constructed'] as $property) { + foreach (['dumpProperties', 'constructedObjectId', 'constructed', 'originalInput'] as $property) { if (isset($infos[$property])) { unset($infos[$property]); } @@ -449,21 +185,31 @@ public function __debugInfo() $this->addExtraDebugInfos($infos); + foreach (["\0*\0", ''] as $prefix) { + $key = $prefix.'carbonRecurrences'; + + if (\array_key_exists($key, $infos)) { + $infos['recurrences'] = $infos[$key]; + unset($infos[$key]); + } + } + return $infos; } - protected function addExtraDebugInfos(&$infos): void + protected function isLocalStrictModeEnabled(): bool + { + return $this->localStrictModeEnabled + ?? $this->transmitFactory(static fn () => static::isStrictModeEnabled()); + } + + protected function addExtraDebugInfos(array &$infos): void { if ($this instanceof DateTimeInterface) { try { - if (!isset($infos['date'])) { - $infos['date'] = $this->format(CarbonInterface::MOCK_DATETIME_FORMAT); - } - - if (!isset($infos['timezone'])) { - $infos['timezone'] = $this->tzName; - } - } catch (Throwable $exception) { + $infos['date'] ??= $this->format(CarbonInterface::MOCK_DATETIME_FORMAT); + $infos['timezone'] ??= $this->tzName ?? $this->timezoneSetting ?? $this->timezone ?? null; + } catch (Throwable) { // noop } } diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Rounding.php b/vendor/nesbot/carbon/src/Carbon/Traits/Rounding.php index 85ff5a711..4239c6689 100644 --- a/vendor/nesbot/carbon/src/Carbon/Traits/Rounding.php +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Rounding.php @@ -1,5 +1,7 @@ [static::YEARS_PER_MILLENNIUM, 'year'], @@ -113,7 +114,7 @@ public function roundUnit($unit, $precision = 1, $function = 'round') } $changes[$unit] = round( - $minimum + ($fraction ? $fraction * $function(($this->$unit - $minimum) / $fraction) : 0) + $minimum + ($fraction ? $fraction * $function(($this->$unit - $minimum) / $fraction) : 0), ); // Cannot use modulo as it lose double precision @@ -140,63 +141,40 @@ public function roundUnit($unit, $precision = 1, $function = 'round') /** * Truncate the current instance at the given unit with given precision if specified. - * - * @param string $unit - * @param float|int $precision - * - * @return CarbonInterface */ - public function floorUnit($unit, $precision = 1) + public function floorUnit(string $unit, DateInterval|string|float|int $precision = 1): static { return $this->roundUnit($unit, $precision, 'floor'); } /** * Ceil the current instance at the given unit with given precision if specified. - * - * @param string $unit - * @param float|int $precision - * - * @return CarbonInterface */ - public function ceilUnit($unit, $precision = 1) + public function ceilUnit(string $unit, DateInterval|string|float|int $precision = 1): static { return $this->roundUnit($unit, $precision, 'ceil'); } /** * Round the current instance second with given precision if specified. - * - * @param float|int|string|\DateInterval|null $precision - * @param string $function - * - * @return CarbonInterface */ - public function round($precision = 1, $function = 'round') + public function round(DateInterval|string|float|int $precision = 1, callable|string $function = 'round'): static { return $this->roundWith($precision, $function); } /** * Round the current instance second with given precision if specified. - * - * @param float|int|string|\DateInterval|null $precision - * - * @return CarbonInterface */ - public function floor($precision = 1) + public function floor(DateInterval|string|float|int $precision = 1): static { return $this->round($precision, 'floor'); } /** * Ceil the current instance second with given precision if specified. - * - * @param float|int|string|\DateInterval|null $precision - * - * @return CarbonInterface */ - public function ceil($precision = 1) + public function ceil(DateInterval|string|float|int $precision = 1): static { return $this->round($precision, 'ceil'); } @@ -204,26 +182,22 @@ public function ceil($precision = 1) /** * Round the current instance week. * - * @param int $weekStartsAt optional start allow you to specify the day of week to use to start the week - * - * @return CarbonInterface + * @param WeekDay|int|null $weekStartsAt optional start allow you to specify the day of week to use to start the week */ - public function roundWeek($weekStartsAt = null) + public function roundWeek(WeekDay|int|null $weekStartsAt = null): static { return $this->closest( $this->avoidMutation()->floorWeek($weekStartsAt), - $this->avoidMutation()->ceilWeek($weekStartsAt) + $this->avoidMutation()->ceilWeek($weekStartsAt), ); } /** * Truncate the current instance week. * - * @param int $weekStartsAt optional start allow you to specify the day of week to use to start the week - * - * @return CarbonInterface + * @param WeekDay|int|null $weekStartsAt optional start allow you to specify the day of week to use to start the week */ - public function floorWeek($weekStartsAt = null) + public function floorWeek(WeekDay|int|null $weekStartsAt = null): static { return $this->startOfWeek($weekStartsAt); } @@ -231,11 +205,9 @@ public function floorWeek($weekStartsAt = null) /** * Ceil the current instance week. * - * @param int $weekStartsAt optional start allow you to specify the day of week to use to start the week - * - * @return CarbonInterface + * @param WeekDay|int|null $weekStartsAt optional start allow you to specify the day of week to use to start the week */ - public function ceilWeek($weekStartsAt = null) + public function ceilWeek(WeekDay|int|null $weekStartsAt = null): static { if ($this->isMutable()) { $startOfWeek = $this->avoidMutation()->startOfWeek($weekStartsAt); diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Serialization.php b/vendor/nesbot/carbon/src/Carbon/Traits/Serialization.php index c1d5c5e13..1cb5c824c 100644 --- a/vendor/nesbot/carbon/src/Carbon/Traits/Serialization.php +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Serialization.php @@ -1,5 +1,7 @@ [Carbon::class, CarbonImmutable::class]]); + * ``` + * + * @param \Stringable|string $value + * @param array $options example: ['allowed_classes' => [CarbonImmutable::class]] * * @throws InvalidFormatException * * @return static */ - public static function fromSerialized($value) + public static function fromSerialized($value, array $options = []): static { - $instance = @unserialize((string) $value); + $instance = @unserialize((string) $value, $options); if (!$instance instanceof static) { throw new InvalidFormatException("Invalid serialized value: $value"); @@ -103,7 +107,7 @@ public static function fromSerialized($value) * @return static */ #[ReturnTypeWillChange] - public static function __set_state($dump) + public static function __set_state($dump): static { if (\is_string($dump)) { return static::parse($dump); @@ -148,9 +152,9 @@ public function __serialize(): array // @codeCoverageIgnoreStart if (isset($this->timezone_type, $this->timezone, $this->date)) { return [ - 'date' => $this->date ?? null, + 'date' => $this->date, 'timezone_type' => $this->timezone_type, - 'timezone' => $this->timezone ?? null, + 'timezone' => $this->dumpTimezone($this->timezone), ]; } // @codeCoverageIgnoreEnd @@ -164,9 +168,10 @@ public function __serialize(): array // @codeCoverageIgnoreStart if (\extension_loaded('msgpack') && isset($this->constructedObjectId)) { + $timezone = $this->timezone ?? null; $export['dumpDateProperties'] = [ 'date' => $this->format('Y-m-d H:i:s.u'), - 'timezone' => serialize($this->timezone ?? null), + 'timezone' => $this->dumpTimezone($timezone), ]; } // @codeCoverageIgnoreEnd @@ -182,11 +187,8 @@ public function __serialize(): array * Set locale if specified on unserialize() called. * * Only used by PHP < 7.4. - * - * @return void */ - #[ReturnTypeWillChange] - public function __wakeup() + public function __wakeup(): void { if (parent::class && method_exists(parent::class, '__wakeup')) { // @codeCoverageIgnoreStart @@ -196,8 +198,8 @@ public function __wakeup() try { // FatalError occurs when calling msgpack_unpack() in PHP 7.4 or later. ['date' => $date, 'timezone' => $timezone] = $this->dumpDateProperties; - parent::__construct($date, unserialize($timezone)); - } catch (Throwable $ignoredException) { + parent::__construct($date, $timezone); + } catch (Throwable) { throw $exception; } } @@ -218,8 +220,6 @@ public function __wakeup() * Set locale if specified on unserialize() called. * * Only used by PHP >= 7.4. - * - * @return void */ public function __unserialize(array $data): void { @@ -234,8 +234,8 @@ public function __unserialize(array $data): void try { // FatalError occurs when calling msgpack_unpack() in PHP 7.4 or later. ['date' => $date, 'timezone' => $timezone] = $data['dumpDateProperties']; - $this->__construct($date, unserialize($timezone)); - } catch (Throwable $ignoredException) { + $this->__construct($date, $timezone); + } catch (Throwable) { throw $exception; } } @@ -248,13 +248,12 @@ public function __unserialize(array $data): void /** * Prepare the object for JSON serialization. - * - * @return array|string */ - #[ReturnTypeWillChange] - public function jsonSerialize() + public function jsonSerialize(): mixed { - $serializer = $this->localSerializer ?? static::$serializer; + $serializer = $this->localSerializer + ?? $this->getFactory()->getSettings()['toJsonFormat'] + ?? null; if ($serializer) { return \is_string($serializer) @@ -270,14 +269,10 @@ public function jsonSerialize() * You should rather transform Carbon object before the serialization. * * JSON serialize all Carbon instances using the given callback. - * - * @param callable $callback - * - * @return void */ - public static function serializeUsing($callback) + public static function serializeUsing(string|callable|null $format): void { - static::$serializer = $callback; + FactoryImmutable::getDefaultInstance()->serializeUsing($format); } /** @@ -287,7 +282,7 @@ public static function serializeUsing($callback) * var_export($date) * get_object_vars($date) */ - public function cleanupDumpProperties() + public function cleanupDumpProperties(): self { // @codeCoverageIgnoreStart if (PHP_VERSION < 8.2) { @@ -312,9 +307,10 @@ private function getSleepProperties(): array } if (isset($this->constructedObjectId)) { + $timezone = $this->timezone ?? null; $this->dumpDateProperties = [ 'date' => $this->format('Y-m-d H:i:s.u'), - 'timezone' => serialize($this->timezone ?? null), + 'timezone' => $this->dumpTimezone($timezone), ]; $properties[] = 'dumpDateProperties'; @@ -323,4 +319,10 @@ private function getSleepProperties(): array return $properties; // @codeCoverageIgnoreEnd } + + /** @codeCoverageIgnore */ + private function dumpTimezone(mixed $timezone): mixed + { + return $timezone instanceof DateTimeZone ? $timezone->getName() : $timezone; + } } diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/StaticLocalization.php b/vendor/nesbot/carbon/src/Carbon/Traits/StaticLocalization.php new file mode 100644 index 000000000..cb1e9e601 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/StaticLocalization.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +use Carbon\FactoryImmutable; +use Symfony\Contracts\Translation\TranslatorInterface; + +/** + * Static config for localization. + */ +trait StaticLocalization +{ + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * @see settings + */ + public static function setHumanDiffOptions(int $humanDiffOptions): void + { + FactoryImmutable::getDefaultInstance()->setHumanDiffOptions($humanDiffOptions); + } + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * @see settings + */ + public static function enableHumanDiffOption(int $humanDiffOption): void + { + FactoryImmutable::getDefaultInstance()->enableHumanDiffOption($humanDiffOption); + } + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * @see settings + */ + public static function disableHumanDiffOption(int $humanDiffOption): void + { + FactoryImmutable::getDefaultInstance()->disableHumanDiffOption($humanDiffOption); + } + + /** + * Return default humanDiff() options (merged flags as integer). + */ + public static function getHumanDiffOptions(): int + { + return FactoryImmutable::getInstance()->getHumanDiffOptions(); + } + + /** + * Set the default translator instance to use. + * + * @param TranslatorInterface $translator + * + * @return void + */ + public static function setTranslator(TranslatorInterface $translator): void + { + FactoryImmutable::getDefaultInstance()->setTranslator($translator); + } + + /** + * Initialize the default translator instance if necessary. + */ + public static function getTranslator(): TranslatorInterface + { + return FactoryImmutable::getInstance()->getTranslator(); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/StaticOptions.php b/vendor/nesbot/carbon/src/Carbon/Traits/StaticOptions.php new file mode 100644 index 000000000..44dd284e1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/StaticOptions.php @@ -0,0 +1,164 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +use Carbon\FactoryImmutable; + +/** + * Options related to a static variable. + */ +trait StaticOptions +{ + /////////////////////////////////////////////////////////////////// + ///////////// Behavior customization for sub-classes ////////////// + /////////////////////////////////////////////////////////////////// + + /** + * Function to call instead of format. + * + * @var string|callable|null + */ + protected static $formatFunction; + + /** + * Function to call instead of createFromFormat. + * + * @var string|callable|null + */ + protected static $createFromFormatFunction; + + /** + * Function to call instead of parse. + * + * @var string|callable|null + */ + protected static $parseFunction; + + /////////////////////////////////////////////////////////////////// + ///////////// Use default factory for static options ////////////// + /////////////////////////////////////////////////////////////////// + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * @see settings + * + * Enable the strict mode (or disable with passing false). + * + * @param bool $strictModeEnabled + */ + public static function useStrictMode(bool $strictModeEnabled = true): void + { + FactoryImmutable::getDefaultInstance()->useStrictMode($strictModeEnabled); + } + + /** + * Returns true if the strict mode is globally in use, false else. + * (It can be overridden in specific instances.) + * + * @return bool + */ + public static function isStrictModeEnabled(): bool + { + return FactoryImmutable::getInstance()->isStrictModeEnabled(); + } + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants + * are available for quarters, years, decade, centuries, millennia (singular and plural forms). + * @see settings + * + * Indicates if months should be calculated with overflow. + * + * @param bool $monthsOverflow + * + * @return void + */ + public static function useMonthsOverflow(bool $monthsOverflow = true): void + { + FactoryImmutable::getDefaultInstance()->useMonthsOverflow($monthsOverflow); + } + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants + * are available for quarters, years, decade, centuries, millennia (singular and plural forms). + * @see settings + * + * Reset the month overflow behavior. + * + * @return void + */ + public static function resetMonthsOverflow(): void + { + FactoryImmutable::getDefaultInstance()->resetMonthsOverflow(); + } + + /** + * Get the month overflow global behavior (can be overridden in specific instances). + * + * @return bool + */ + public static function shouldOverflowMonths(): bool + { + return FactoryImmutable::getInstance()->shouldOverflowMonths(); + } + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants + * are available for quarters, years, decade, centuries, millennia (singular and plural forms). + * @see settings + * + * Indicates if years should be calculated with overflow. + * + * @param bool $yearsOverflow + * + * @return void + */ + public static function useYearsOverflow(bool $yearsOverflow = true): void + { + FactoryImmutable::getDefaultInstance()->useYearsOverflow($yearsOverflow); + } + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants + * are available for quarters, years, decade, centuries, millennia (singular and plural forms). + * @see settings + * + * Reset the month overflow behavior. + * + * @return void + */ + public static function resetYearsOverflow(): void + { + FactoryImmutable::getDefaultInstance()->resetYearsOverflow(); + } + + /** + * Get the month overflow global behavior (can be overridden in specific instances). + * + * @return bool + */ + public static function shouldOverflowYears(): bool + { + return FactoryImmutable::getInstance()->shouldOverflowYears(); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Test.php b/vendor/nesbot/carbon/src/Carbon/Traits/Test.php index f23c72e8f..c642dbdbf 100644 --- a/vendor/nesbot/carbon/src/Carbon/Traits/Test.php +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Test.php @@ -1,5 +1,7 @@ setTestNow($testNow); } /** @@ -87,29 +74,9 @@ public static function setTestNow($testNow = null) * * @param DateTimeInterface|Closure|static|string|false|null $testNow real or mock Carbon instance */ - public static function setTestNowAndTimezone($testNow = null, $tz = null) + public static function setTestNowAndTimezone($testNow = null, $timezone = null): void { - if ($testNow) { - self::$testDefaultTimezone = self::$testDefaultTimezone ?? date_default_timezone_get(); - } - - $useDateInstanceTimezone = $testNow instanceof DateTimeInterface; - - if ($useDateInstanceTimezone) { - self::setDefaultTimezone($testNow->getTimezone()->getName(), $testNow); - } - - static::setTestNow($testNow); - - if (!$useDateInstanceTimezone) { - $now = static::getMockedTestNow(\func_num_args() === 1 ? null : $tz); - $tzName = $now ? $now->tzName : null; - self::setDefaultTimezone($tzName ?? self::$testDefaultTimezone ?? 'UTC', $now); - } - - if (!$testNow) { - self::$testDefaultTimezone = null; - } + FactoryImmutable::getDefaultInstance()->setTestNowAndTimezone($testNow, $timezone); } /** @@ -126,28 +93,20 @@ public static function setTestNowAndTimezone($testNow = null, $tz = null) * * @return T */ - public static function withTestNow($testNow, $callback) + public static function withTestNow(mixed $testNow, callable $callback): mixed { - static::setTestNow($testNow); - - try { - $result = $callback(); - } finally { - static::setTestNow(); - } - - return $result; + return FactoryImmutable::getDefaultInstance()->withTestNow($testNow, $callback); } /** * Get the Carbon instance (real or mock) to be returned when a "now" * instance is created. * - * @return Closure|static the current instance used for testing + * @return Closure|CarbonInterface|null the current instance used for testing */ - public static function getTestNow() + public static function getTestNow(): Closure|CarbonInterface|null { - return static::$testNow; + return FactoryImmutable::getInstance()->getTestNow(); } /** @@ -156,73 +115,71 @@ public static function getTestNow() * * @return bool true if there is a test instance, otherwise false */ - public static function hasTestNow() + public static function hasTestNow(): bool { - return static::getTestNow() !== null; + return FactoryImmutable::getInstance()->hasTestNow(); } /** * Get the mocked date passed in setTestNow() and if it's a Closure, execute it. - * - * @param string|\DateTimeZone $tz - * - * @return \Carbon\CarbonImmutable|\Carbon\Carbon|null */ - protected static function getMockedTestNow($tz) + protected static function getMockedTestNow(DateTimeZone|string|int|null $timezone): ?CarbonInterface { - $testNow = static::getTestNow(); - - if ($testNow instanceof Closure) { - $realNow = new DateTimeImmutable('now'); - $testNow = $testNow(static::parse( - $realNow->format('Y-m-d H:i:s.u'), - $tz ?: $realNow->getTimezone() - )); + $testNow = FactoryImmutable::getInstance()->handleTestNowClosure(static::getTestNow(), $timezone); + + if ($testNow === null) { + return null; } - /* @var \Carbon\CarbonImmutable|\Carbon\Carbon|null $testNow */ - return $testNow instanceof CarbonInterface - ? $testNow->avoidMutation()->tz($tz) - : $testNow; + $testNow = $testNow->avoidMutation(); + + return $timezone ? $testNow->setTimezone($timezone) : $testNow; } - protected static function mockConstructorParameters(&$time, $tz) + private function mockConstructorParameters(&$time, ?CarbonTimeZone $timezone): void { - /** @var \Carbon\CarbonImmutable|\Carbon\Carbon $testInstance */ - $testInstance = clone static::getMockedTestNow($tz); + $clock = $this->clock?->unwrap(); + $now = $clock instanceof Factory + ? $clock->getTestNow() + : $this->nowFromClock($timezone); + $testInstance = $now ?? self::getMockedTestNowClone($timezone); + + if (!$testInstance) { + return; + } + + if ($testInstance instanceof DateTimeInterface) { + $testInstance = $testInstance->setTimezone($timezone ?? date_default_timezone_get()); + } if (static::hasRelativeKeywords($time)) { $testInstance = $testInstance->modify($time); } + $factory = $this->getClock()?->unwrap(); + + if (!($factory instanceof Factory)) { + $factory = FactoryImmutable::getInstance(); + } + + $testInstance = $factory->handleTestNowClosure($testInstance, $timezone); + $time = $testInstance instanceof self ? $testInstance->rawFormat(static::MOCK_DATETIME_FORMAT) : $testInstance->format(static::MOCK_DATETIME_FORMAT); } - private static function setDefaultTimezone($timezone, DateTimeInterface $date = null) + private function getMockedTestNowClone($timezone): CarbonInterface|self|null { - $previous = null; - $success = false; + $mock = static::getMockedTestNow($timezone); - try { - $success = date_default_timezone_set($timezone); - } catch (Throwable $exception) { - $previous = $exception; - } + return $mock ? clone $mock : null; + } - if (!$success) { - $suggestion = @CarbonTimeZone::create($timezone)->toRegionName($date); - - throw new InvalidArgumentException( - "Timezone ID '$timezone' is invalid". - ($suggestion && $suggestion !== $timezone ? ", did you mean '$suggestion'?" : '.')."\n". - "It must be one of the IDs from DateTimeZone::listIdentifiers(),\n". - 'For the record, hours/minutes offset are relevant only for a particular moment, '. - 'but not as a default timezone.', - 0, - $previous - ); - } + private function nowFromClock(?CarbonTimeZone $timezone): ?DateTimeImmutable + { + $now = $this->clock?->now(); + + return $now && $timezone ? $now->setTimezone($timezone) : null; } } diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Timestamp.php b/vendor/nesbot/carbon/src/Carbon/Traits/Timestamp.php index 88a465c93..dab448dec 100644 --- a/vendor/nesbot/carbon/src/Carbon/Traits/Timestamp.php +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Timestamp.php @@ -1,5 +1,7 @@ setTimezone($tz); + #[\ReturnTypeWillChange] + public static function createFromTimestamp( + float|int|string $timestamp, + DateTimeZone|string|int|null $timezone = null, + ): static { + $date = static::createFromTimestampUTC($timestamp); + + return $timezone === null ? $date : $date->setTimezone($timezone); } /** - * Create a Carbon instance from an timestamp keeping the timezone to UTC. + * Create a Carbon instance from a timestamp keeping the timezone to UTC. * * Timestamp input can be given as int, float or a string containing one or more numbers. - * - * @param float|int|string $timestamp - * - * @return static */ - public static function createFromTimestampUTC($timestamp) + public static function createFromTimestampUTC(float|int|string $timestamp): static { [$integer, $decimal] = self::getIntegerAndDecimalParts($timestamp); $delta = floor($decimal / static::MICROSECONDS_PER_SECOND); @@ -60,7 +60,7 @@ public static function createFromTimestampUTC($timestamp) * * @return static */ - public static function createFromTimestampMsUTC($timestamp) + public static function createFromTimestampMsUTC($timestamp): static { [$milliseconds, $microseconds] = self::getIntegerAndDecimalParts($timestamp, 3); $sign = $milliseconds < 0 || ($milliseconds === 0.0 && $microseconds < 0) ? -1 : 1; @@ -68,9 +68,9 @@ public static function createFromTimestampMsUTC($timestamp) $microseconds = $sign * abs($microseconds) + static::MICROSECONDS_PER_MILLISECOND * ($milliseconds % static::MILLISECONDS_PER_SECOND); $seconds = $sign * floor($milliseconds / static::MILLISECONDS_PER_SECOND); $delta = floor($microseconds / static::MICROSECONDS_PER_SECOND); - $seconds += $delta; + $seconds = (int) ($seconds + $delta); $microseconds -= $delta * static::MICROSECONDS_PER_SECOND; - $microseconds = str_pad($microseconds, 6, '0', STR_PAD_LEFT); + $microseconds = str_pad((string) (int) $microseconds, 6, '0', STR_PAD_LEFT); return static::rawCreateFromFormat('U u', "$seconds $microseconds"); } @@ -79,30 +79,24 @@ public static function createFromTimestampMsUTC($timestamp) * Create a Carbon instance from a timestamp in milliseconds. * * Timestamp input can be given as int, float or a string containing one or more numbers. - * - * @param float|int|string $timestamp - * @param \DateTimeZone|string|null $tz - * - * @return static */ - public static function createFromTimestampMs($timestamp, $tz = null) - { - return static::createFromTimestampMsUTC($timestamp) - ->setTimezone($tz); + public static function createFromTimestampMs( + float|int|string $timestamp, + DateTimeZone|string|int|null $timezone = null, + ): static { + $date = static::createFromTimestampMsUTC($timestamp); + + return $timezone === null ? $date : $date->setTimezone($timezone); } /** * Set the instance's timestamp. * * Timestamp input can be given as int, float or a string containing one or more numbers. - * - * @param float|int|string $unixTimestamp - * - * @return static */ - public function timestamp($unixTimestamp) + public function timestamp(float|int|string $timestamp): static { - return $this->setTimestamp($unixTimestamp); + return $this->setTimestamp($timestamp); } /** @@ -123,7 +117,7 @@ public function timestamp($unixTimestamp) * * @return float */ - public function getPreciseTimestamp($precision = 6) + public function getPreciseTimestamp($precision = 6): float { return round(((float) $this->rawFormat('Uu')) / pow(10, 6 - $precision)); } @@ -133,7 +127,7 @@ public function getPreciseTimestamp($precision = 6) * * @return float */ - public function valueOf() + public function valueOf(): float { return $this->getPreciseTimestamp(3); } @@ -143,7 +137,7 @@ public function valueOf() * * @return int */ - public function getTimestampMs() + public function getTimestampMs(): int { return (int) $this->getPreciseTimestamp(3); } @@ -155,7 +149,7 @@ public function getTimestampMs() * * @return int */ - public function unix() + public function unix(): int { return $this->getTimestamp(); } @@ -172,7 +166,7 @@ public function unix() * * @return array 0-index is integer part, 1-index is decimal part digits */ - private static function getIntegerAndDecimalParts($numbers, $decimals = 6) + private static function getIntegerAndDecimalParts($numbers, $decimals = 6): array { if (\is_int($numbers) || \is_float($numbers)) { $numbers = number_format($numbers, $decimals, '.', ''); diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/ToStringFormat.php b/vendor/nesbot/carbon/src/Carbon/Traits/ToStringFormat.php index a81164f99..5f0b367f0 100644 --- a/vendor/nesbot/carbon/src/Carbon/Traits/ToStringFormat.php +++ b/vendor/nesbot/carbon/src/Carbon/Traits/ToStringFormat.php @@ -1,5 +1,7 @@ resetToStringFormat(); } /** @@ -49,8 +45,8 @@ public static function resetToStringFormat() * * @return void */ - public static function setToStringFormat($format) + public static function setToStringFormat(string|Closure|null $format): void { - static::$toStringFormat = $format; + FactoryImmutable::getDefaultInstance()->setToStringFormat($format); } } diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Units.php b/vendor/nesbot/carbon/src/Carbon/Traits/Units.php index 5be14ec7e..595372142 100644 --- a/vendor/nesbot/carbon/src/Carbon/Traits/Units.php +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Units.php @@ -1,5 +1,7 @@ addUTCUnit($unit, $value); + } + + /** + * Add seconds to the instance using timestamp. Positive $value travels + * forward while negative $value travels into the past. + * + * @param string $unit + * @param int|float|null $value * * @return static */ - public function addRealUnit($unit, $value = 1) + public function addUTCUnit(string $unit, $value = 1): static { + $value ??= 0; + switch ($unit) { - // @call addRealUnit + // @call addUTCUnit case 'micro': - // @call addRealUnit + // @call addUTCUnit case 'microsecond': /* @var CarbonInterface $this */ $diff = $this->microsecond + $value; @@ -51,95 +75,125 @@ public function addRealUnit($unit, $value = 1) $time += $seconds; $diff -= $seconds * static::MICROSECONDS_PER_SECOND; $microtime = str_pad((string) $diff, 6, '0', STR_PAD_LEFT); - $tz = $this->tz; + $timezone = $this->tz; - return $this->tz('UTC')->modify("@$time.$microtime")->tz($tz); + return $this->tz('UTC')->modify("@$time.$microtime")->setTimezone($timezone); - // @call addRealUnit + // @call addUTCUnit case 'milli': - // @call addRealUnit + // @call addUTCUnit case 'millisecond': - return $this->addRealUnit('microsecond', $value * static::MICROSECONDS_PER_MILLISECOND); + return $this->addUTCUnit('microsecond', $value * static::MICROSECONDS_PER_MILLISECOND); - // @call addRealUnit + // @call addUTCUnit case 'second': break; - // @call addRealUnit + // @call addUTCUnit case 'minute': $value *= static::SECONDS_PER_MINUTE; break; - // @call addRealUnit + // @call addUTCUnit case 'hour': $value *= static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE; break; - // @call addRealUnit + // @call addUTCUnit case 'day': $value *= static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE; break; - // @call addRealUnit + // @call addUTCUnit case 'week': $value *= static::DAYS_PER_WEEK * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE; break; - // @call addRealUnit + // @call addUTCUnit case 'month': $value *= 30 * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE; break; - // @call addRealUnit + // @call addUTCUnit case 'quarter': $value *= static::MONTHS_PER_QUARTER * 30 * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE; break; - // @call addRealUnit + // @call addUTCUnit case 'year': $value *= 365 * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE; break; - // @call addRealUnit + // @call addUTCUnit case 'decade': $value *= static::YEARS_PER_DECADE * 365 * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE; break; - // @call addRealUnit + // @call addUTCUnit case 'century': $value *= static::YEARS_PER_CENTURY * 365 * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE; break; - // @call addRealUnit + // @call addUTCUnit case 'millennium': $value *= static::YEARS_PER_MILLENNIUM * 365 * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE; break; default: - if ($this->localStrictModeEnabled ?? static::isStrictModeEnabled()) { + if ($this->isLocalStrictModeEnabled()) { throw new UnitException("Invalid unit for real timestamp add/sub: '$unit'"); } return $this; } - /* @var CarbonInterface $this */ - return $this->setTimestamp((int) ($this->getTimestamp() + $value)); + $seconds = (int) $value; + $microseconds = (int) round( + (abs($value) - abs($seconds)) * ($value < 0 ? -1 : 1) * static::MICROSECONDS_PER_SECOND, + ); + $date = $this->setTimestamp($this->getTimestamp() + $seconds); + + return $microseconds ? $date->addUTCUnit('microsecond', $microseconds) : $date; } - public function subRealUnit($unit, $value = 1) + /** + * @deprecated Prefer to use add subUTCUnit() which more accurately defines what it's doing. + * + * Subtract seconds to the instance using timestamp. Positive $value travels + * into the past while negative $value travels forward. + * + * @param string $unit + * @param int $value + * + * @return static + */ + public function subRealUnit($unit, $value = 1): static { - return $this->addRealUnit($unit, -$value); + return $this->addUTCUnit($unit, -$value); + } + + /** + * Subtract seconds to the instance using timestamp. Positive $value travels + * into the past while negative $value travels forward. + * + * @param string $unit + * @param int $value + * + * @return static + */ + public function subUTCUnit($unit, $value = 1): static + { + return $this->addUTCUnit($unit, -$value); } /** @@ -149,7 +203,7 @@ public function subRealUnit($unit, $value = 1) * * @return bool */ - public static function isModifiableUnit($unit) + public static function isModifiableUnit($unit): bool { static $modifiableUnits = [ // @call addUnit @@ -176,7 +230,7 @@ public static function isModifiableUnit($unit) * * @return static */ - public function rawAdd(DateInterval $interval) + public function rawAdd(DateInterval $interval): static { return parent::add($interval); } @@ -188,25 +242,34 @@ public function rawAdd(DateInterval $interval) * @example $date->add(15, 'days') * @example $date->add(CarbonInterval::days(4)) * - * @param string|DateInterval|Closure|CarbonConverterInterface $unit - * @param int $value - * @param bool|null $overflow + * @param Unit|string|DateInterval|Closure|CarbonConverterInterface $unit + * @param int|float $value + * @param bool|null $overflow * * @return static */ #[ReturnTypeWillChange] - public function add($unit, $value = 1, $overflow = null) + public function add($unit, $value = 1, ?bool $overflow = null): static { + $unit = Unit::toNameIfUnit($unit); + $value = Unit::toNameIfUnit($value); + if (\is_string($unit) && \func_num_args() === 1) { $unit = CarbonInterval::make($unit, [], true); } if ($unit instanceof CarbonConverterInterface) { - return $this->resolveCarbon($unit->convertDate($this, false)); + $unit = Closure::fromCallable([$unit, 'convertDate']); } if ($unit instanceof Closure) { - return $this->resolveCarbon($unit($this, false)); + $result = $this->resolveCarbon($unit($this, false)); + + if ($this !== $result && $this->isMutable()) { + return $this->modify($result->rawFormat('Y-m-d H:i:s.u e O')); + } + + return $result; } if ($unit instanceof DateInterval) { @@ -217,26 +280,22 @@ public function add($unit, $value = 1, $overflow = null) [$value, $unit] = [$unit, $value]; } - return $this->addUnit($unit, $value, $overflow); + return $this->addUnit((string) $unit, $value, $overflow); } /** * Add given units to the current instance. - * - * @param string $unit - * @param int $value - * @param bool|null $overflow - * - * @return static */ - public function addUnit($unit, $value = 1, $overflow = null) + public function addUnit(Unit|string $unit, $value = 1, ?bool $overflow = null): static { + $unit = Unit::toName($unit); + $originalArgs = \func_get_args(); $date = $this; if (!is_numeric($value) || !(float) $value) { - return $date->isMutable() ? $date : $date->avoidMutation(); + return $date->isMutable() ? $date : $date->copy(); } $unit = self::singularUnit($unit); @@ -253,12 +312,12 @@ public function addUnit($unit, $value = 1, $overflow = null) } if ($unit === 'weekday') { - $weekendDays = static::getWeekendDays(); + $weekendDays = $this->transmitFactory(static fn () => static::getWeekendDays()); if ($weekendDays !== [static::SATURDAY, static::SUNDAY]) { $absoluteValue = abs($value); $sign = $value / max(1, $absoluteValue); - $weekDaysCount = 7 - min(6, \count(array_unique($weekendDays))); + $weekDaysCount = static::DAYS_PER_WEEK - min(static::DAYS_PER_WEEK - 1, \count(array_unique($weekendDays))); $weeks = floor($absoluteValue / $weekDaysCount); for ($diff = $absoluteValue % $weekDaysCount; $diff; $diff--) { @@ -286,67 +345,44 @@ public function addUnit($unit, $value = 1, $overflow = null) $day = $date->day; } - $value = (int) $value; - if ($unit === 'milli' || $unit === 'millisecond') { $unit = 'microsecond'; $value *= static::MICROSECONDS_PER_MILLISECOND; } - // Work-around for bug https://bugs.php.net/bug.php?id=75642 - if ($unit === 'micro' || $unit === 'microsecond') { - $microseconds = $this->micro + $value; - $second = (int) floor($microseconds / static::MICROSECONDS_PER_SECOND); - $microseconds %= static::MICROSECONDS_PER_SECOND; - if ($microseconds < 0) { - $microseconds += static::MICROSECONDS_PER_SECOND; - } - $date = $date->microseconds($microseconds); - $unit = 'second'; - $value = $second; - } + $previousException = null; try { - $date = $date->modify("$value $unit"); + $date = self::rawAddUnit($date, $unit, $value); if (isset($timeString)) { - $date = $date->setTimeFromTimeString($timeString); - } elseif (isset($canOverflow, $day) && $canOverflow && $day !== $date->day) { - $date = $date->modify('last day of previous month'); + $date = $date?->setTimeFromTimeString($timeString); + } elseif (isset($canOverflow, $day) && $canOverflow && $day !== $date?->day) { + $date = $date?->modify('last day of previous month'); } - } catch (DateMalformedStringException $ignoredException) { // @codeCoverageIgnore - $date = null; // @codeCoverageIgnore - } - - if (!$date) { - throw new UnitException('Unable to add unit '.var_export($originalArgs, true)); + } catch (DateMalformedStringException|InvalidFormatException|UnsupportedUnitException $exception) { + $date = null; + $previousException = $exception; } - return $date; + return $date ?? throw new UnitException( + 'Unable to add unit '.var_export($originalArgs, true), + previous: $previousException, + ); } /** * Subtract given units to the current instance. - * - * @param string $unit - * @param int $value - * @param bool|null $overflow - * - * @return static */ - public function subUnit($unit, $value = 1, $overflow = null) + public function subUnit(Unit|string $unit, $value = 1, ?bool $overflow = null): static { return $this->addUnit($unit, -$value, $overflow); } /** * Call native PHP DateTime/DateTimeImmutable sub() method. - * - * @param DateInterval $interval - * - * @return static */ - public function rawSub(DateInterval $interval) + public function rawSub(DateInterval $interval): static { return parent::sub($interval); } @@ -358,25 +394,31 @@ public function rawSub(DateInterval $interval) * @example $date->sub(15, 'days') * @example $date->sub(CarbonInterval::days(4)) * - * @param string|DateInterval|Closure|CarbonConverterInterface $unit - * @param int $value - * @param bool|null $overflow + * @param Unit|string|DateInterval|Closure|CarbonConverterInterface $unit + * @param int|float $value + * @param bool|null $overflow * * @return static */ #[ReturnTypeWillChange] - public function sub($unit, $value = 1, $overflow = null) + public function sub($unit, $value = 1, ?bool $overflow = null): static { if (\is_string($unit) && \func_num_args() === 1) { $unit = CarbonInterval::make($unit, [], true); } if ($unit instanceof CarbonConverterInterface) { - return $this->resolveCarbon($unit->convertDate($this, true)); + $unit = Closure::fromCallable([$unit, 'convertDate']); } if ($unit instanceof Closure) { - return $this->resolveCarbon($unit($this, true)); + $result = $this->resolveCarbon($unit($this, true)); + + if ($this !== $result && $this->isMutable()) { + return $this->modify($result->rawFormat('Y-m-d H:i:s.u e O')); + } + + return $result; } if ($unit instanceof DateInterval) { @@ -387,7 +429,7 @@ public function sub($unit, $value = 1, $overflow = null) [$value, $unit] = [$unit, $value]; } - return $this->addUnit($unit, -(float) $value, $overflow); + return $this->addUnit((string) $unit, -(float) $value, $overflow); } /** @@ -396,12 +438,12 @@ public function sub($unit, $value = 1, $overflow = null) * @see sub() * * @param string|DateInterval $unit - * @param int $value + * @param int|float $value * @param bool|null $overflow * * @return static */ - public function subtract($unit, $value = 1, $overflow = null) + public function subtract($unit, $value = 1, ?bool $overflow = null): static { if (\is_string($unit) && \func_num_args() === 1) { $unit = CarbonInterval::make($unit, [], true); @@ -409,4 +451,19 @@ public function subtract($unit, $value = 1, $overflow = null) return $this->sub($unit, $value, $overflow); } + + private static function rawAddUnit(self $date, string $unit, int|float $value): ?static + { + try { + return $date->rawAdd( + CarbonInterval::fromString(abs($value)." $unit")->invert($value < 0), + ); + } catch (InvalidIntervalException $exception) { + try { + return $date->modify("$value $unit"); + } catch (InvalidFormatException) { + throw new UnsupportedUnitException($unit, previous: $exception); + } + } + } } diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Week.php b/vendor/nesbot/carbon/src/Carbon/Traits/Week.php index 6f1481456..0101d055e 100644 --- a/vendor/nesbot/carbon/src/Carbon/Traits/Week.php +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Week.php @@ -1,5 +1,7 @@ weekYear( $year, - $dayOfWeek ?? 1, - $dayOfYear ?? 4 + $dayOfWeek ?? static::MONDAY, + $dayOfYear ?? static::THURSDAY, ); } @@ -69,7 +73,7 @@ public function isoWeekYear($year = null, $dayOfWeek = null, $dayOfYear = null) */ public function weekYear($year = null, $dayOfWeek = null, $dayOfYear = null) { - $dayOfWeek = $dayOfWeek ?? $this->getTranslationMessage('first_day_of_week') ?? 0; + $dayOfWeek = $dayOfWeek ?? $this->getTranslationMessage('first_day_of_week') ?? static::SUNDAY; $dayOfYear = $dayOfYear ?? $this->getTranslationMessage('day_of_first_week_of_year') ?? 1; if ($year !== null) { @@ -82,18 +86,16 @@ public function weekYear($year = null, $dayOfWeek = null, $dayOfYear = null) $week = $this->week(null, $dayOfWeek, $dayOfYear); $day = $this->dayOfWeek; $date = $this->year($year); - switch ($date->weekYear(null, $dayOfWeek, $dayOfYear) - $year) { - case 1: - $date = $date->subWeeks(26); - - break; - case -1: - $date = $date->addWeeks(26); - break; - } + $date = match ($date->weekYear(null, $dayOfWeek, $dayOfYear) - $year) { + CarbonInterval::POSITIVE => $date->subWeeks(static::WEEKS_PER_YEAR / 2), + CarbonInterval::NEGATIVE => $date->addWeeks(static::WEEKS_PER_YEAR / 2), + default => $date, + }; - $date = $date->addWeeks($week - $date->week(null, $dayOfWeek, $dayOfYear))->startOfWeek($dayOfWeek); + $date = $date + ->addWeeks($week - $date->week(null, $dayOfWeek, $dayOfYear)) + ->startOfWeek($dayOfWeek); if ($date->dayOfWeek === $day) { return $date; @@ -132,8 +134,8 @@ public function weekYear($year = null, $dayOfWeek = null, $dayOfYear = null) public function isoWeeksInYear($dayOfWeek = null, $dayOfYear = null) { return $this->weeksInYear( - $dayOfWeek ?? 1, - $dayOfYear ?? 4 + $dayOfWeek ?? static::MONDAY, + $dayOfYear ?? static::THURSDAY, ); } @@ -149,7 +151,7 @@ public function isoWeeksInYear($dayOfWeek = null, $dayOfYear = null) */ public function weeksInYear($dayOfWeek = null, $dayOfYear = null) { - $dayOfWeek = $dayOfWeek ?? $this->getTranslationMessage('first_day_of_week') ?? 0; + $dayOfWeek = $dayOfWeek ?? $this->getTranslationMessage('first_day_of_week') ?? static::SUNDAY; $dayOfYear = $dayOfYear ?? $this->getTranslationMessage('day_of_first_week_of_year') ?? 1; $year = $this->year; $start = $this->avoidMutation()->dayOfYear($dayOfYear)->startOfWeek($dayOfWeek); @@ -163,7 +165,7 @@ public function weeksInYear($dayOfWeek = null, $dayOfYear = null) $endDay += $this->daysInYear; } - return (int) round(($endDay - $startDay) / 7); + return (int) round(($endDay - $startDay) / static::DAYS_PER_WEEK); } /** @@ -187,12 +189,14 @@ public function week($week = null, $dayOfWeek = null, $dayOfYear = null) return $date->addWeeks(round($week) - $this->week(null, $dayOfWeek, $dayOfYear)); } - $start = $date->avoidMutation()->dayOfYear($dayOfYear)->startOfWeek($dayOfWeek); - $end = $date->avoidMutation()->startOfWeek($dayOfWeek); + $start = $date->avoidMutation()->shiftTimezone('UTC')->dayOfYear($dayOfYear)->startOfWeek($dayOfWeek); + $end = $date->avoidMutation()->shiftTimezone('UTC')->startOfWeek($dayOfWeek); + if ($start > $end) { - $start = $start->subWeeks(26)->dayOfYear($dayOfYear)->startOfWeek($dayOfWeek); + $start = $start->subWeeks(static::WEEKS_PER_YEAR / 2)->dayOfYear($dayOfYear)->startOfWeek($dayOfWeek); } - $week = (int) ($start->diffInDays($end) / 7 + 1); + + $week = (int) ($start->diffInDays($end) / static::DAYS_PER_WEEK + 1); return $week > $end->weeksInYear($dayOfWeek, $dayOfYear) ? 1 : $week; } @@ -212,8 +216,8 @@ public function isoWeek($week = null, $dayOfWeek = null, $dayOfYear = null) { return $this->week( $week, - $dayOfWeek ?? 1, - $dayOfYear ?? 4 + $dayOfWeek ?? static::MONDAY, + $dayOfYear ?? static::THURSDAY, ); } } diff --git a/vendor/nesbot/carbon/src/Carbon/Translator.php b/vendor/nesbot/carbon/src/Carbon/Translator.php index 491c9e720..9f523b264 100644 --- a/vendor/nesbot/carbon/src/Carbon/Translator.php +++ b/vendor/nesbot/carbon/src/Carbon/Translator.php @@ -1,5 +1,7 @@ hasReturnType() diff --git a/vendor/nesbot/carbon/src/Carbon/TranslatorImmutable.php b/vendor/nesbot/carbon/src/Carbon/TranslatorImmutable.php index ce6b2f90a..ab9933e56 100644 --- a/vendor/nesbot/carbon/src/Carbon/TranslatorImmutable.php +++ b/vendor/nesbot/carbon/src/Carbon/TranslatorImmutable.php @@ -1,5 +1,7 @@ constructed = true; @@ -29,24 +30,24 @@ public function __construct($locale, MessageFormatterInterface $formatter = null /** * @codeCoverageIgnore */ - public function setDirectories(array $directories) + public function setDirectories(array $directories): static { $this->disallowMutation(__METHOD__); return parent::setDirectories($directories); } - public function setLocale($locale) + public function setLocale($locale): void { $this->disallowMutation(__METHOD__); - return parent::setLocale($locale); + parent::setLocale($locale); } /** * @codeCoverageIgnore */ - public function setMessages($locale, $messages) + public function setMessages(string $locale, array $messages): static { $this->disallowMutation(__METHOD__); @@ -56,7 +57,7 @@ public function setMessages($locale, $messages) /** * @codeCoverageIgnore */ - public function setTranslations($messages) + public function setTranslations(array $messages): static { $this->disallowMutation(__METHOD__); @@ -73,7 +74,7 @@ public function setConfigCacheFactory(ConfigCacheFactoryInterface $configCacheFa parent::setConfigCacheFactory($configCacheFactory); } - public function resetMessages($locale = null) + public function resetMessages(?string $locale = null): bool { $this->disallowMutation(__METHOD__); @@ -83,7 +84,7 @@ public function resetMessages($locale = null) /** * @codeCoverageIgnore */ - public function setFallbackLocales(array $locales) + public function setFallbackLocales(array $locales): void { $this->disallowMutation(__METHOD__); diff --git a/vendor/nesbot/carbon/src/Carbon/TranslatorStrongTypeInterface.php b/vendor/nesbot/carbon/src/Carbon/TranslatorStrongTypeInterface.php index ef4dee8e2..54a798093 100644 --- a/vendor/nesbot/carbon/src/Carbon/TranslatorStrongTypeInterface.php +++ b/vendor/nesbot/carbon/src/Carbon/TranslatorStrongTypeInterface.php @@ -1,5 +1,7 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +enum Unit: string +{ + case Microsecond = 'microsecond'; + case Millisecond = 'millisecond'; + case Second = 'second'; + case Minute = 'minute'; + case Hour = 'hour'; + case Day = 'day'; + case Week = 'week'; + case Month = 'month'; + case Quarter = 'quarter'; + case Year = 'year'; + case Decade = 'decade'; + case Century = 'century'; + case Millennium = 'millennium'; + + public static function toName(self|string $unit): string + { + return $unit instanceof self ? $unit->value : $unit; + } + + /** @internal */ + public static function toNameIfUnit(mixed $unit): mixed + { + return $unit instanceof self ? $unit->value : $unit; + } + + public static function fromName(string $name, ?string $locale = null): self + { + if ($locale !== null) { + $messages = Translator::get($locale)->getMessages($locale) ?? []; + + if ($messages !== []) { + $lowerName = mb_strtolower($name); + + foreach (self::cases() as $unit) { + foreach (['', '_from_now', '_ago', '_after', '_before'] as $suffix) { + $message = $messages[$unit->value.$suffix] ?? null; + + if (\is_string($message)) { + $words = explode('|', mb_strtolower(preg_replace( + '/[{\[\]].+?[}\[\]]/', + '', + str_replace(':count', '', $message), + ))); + + foreach ($words as $word) { + if (trim($word) === $lowerName) { + return $unit; + } + } + } + } + } + } + } + + return self::from(CarbonImmutable::singularUnit($name)); + } + + public function singular(?string $locale = null): string + { + if ($locale !== null) { + return trim(Translator::get($locale)->trans($this->value, [ + '%count%' => 1, + ':count' => 1, + ]), "1 \n\r\t\v\0"); + } + + return $this->value; + } + + public function plural(?string $locale = null): string + { + if ($locale !== null) { + return trim(Translator::get($locale)->trans($this->value, [ + '%count%' => 9, + ':count' => 9, + ]), "9 \n\r\t\v\0"); + } + + return CarbonImmutable::pluralUnit($this->value); + } + + public function interval(int|float $value = 1): CarbonInterval + { + return CarbonInterval::fromString("$value $this->name"); + } + + public function locale(string $locale): CarbonInterval + { + return $this->interval()->locale($locale); + } + + public function toPeriod(...$params): CarbonPeriod + { + return $this->interval()->toPeriod(...$params); + } + + public function stepBy(mixed $interval, Unit|string|null $unit = null): CarbonPeriod + { + return $this->interval()->stepBy($interval, $unit); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/WeekDay.php b/vendor/nesbot/carbon/src/Carbon/WeekDay.php new file mode 100644 index 000000000..69f69cebc --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/WeekDay.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +use Carbon\Exceptions\InvalidFormatException; + +enum WeekDay: int +{ + // Using constants is only safe starting from PHP 8.2 + case Sunday = 0; // CarbonInterface::SUNDAY + case Monday = 1; // CarbonInterface::MONDAY + case Tuesday = 2; // CarbonInterface::TUESDAY + case Wednesday = 3; // CarbonInterface::WEDNESDAY + case Thursday = 4; // CarbonInterface::THURSDAY + case Friday = 5; // CarbonInterface::FRIDAY + case Saturday = 6; // CarbonInterface::SATURDAY + + public static function int(self|int|null $value): ?int + { + return $value instanceof self ? $value->value : $value; + } + + public static function fromNumber(int $number): self + { + $day = $number % CarbonInterface::DAYS_PER_WEEK; + + return self::from($day + ($day < 0 ? CarbonInterface::DAYS_PER_WEEK : 0)); + } + + public static function fromName(string $name, ?string $locale = null): self + { + try { + return self::from(CarbonImmutable::parseFromLocale($name, $locale)->dayOfWeek); + } catch (InvalidFormatException $exception) { + // Possibly current language expect a dot after short name, but it's missing + if ($locale !== null && !mb_strlen($name) < 4 && !str_ends_with($name, '.')) { + try { + return self::from(CarbonImmutable::parseFromLocale($name.'.', $locale)->dayOfWeek); + } catch (InvalidFormatException) { + // Throw previous error + } + } + + throw $exception; + } + } + + public function next(?CarbonImmutable $now = null): CarbonImmutable + { + return $now?->modify($this->name) ?? new CarbonImmutable($this->name); + } + + public function locale(string $locale, ?CarbonImmutable $now = null): CarbonImmutable + { + return $this->next($now)->locale($locale); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/WrapperClock.php b/vendor/nesbot/carbon/src/Carbon/WrapperClock.php new file mode 100644 index 000000000..7fb184fe5 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/WrapperClock.php @@ -0,0 +1,187 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +use DateTime; +use DateTimeImmutable; +use DateTimeInterface; +use DateTimeZone; +use Psr\Clock\ClockInterface as PsrClockInterface; +use RuntimeException; +use Symfony\Component\Clock\ClockInterface; + +final class WrapperClock implements ClockInterface +{ + public function __construct( + private PsrClockInterface|Factory|DateTimeInterface $currentClock, + ) { + } + + public function unwrap(): PsrClockInterface|Factory|DateTimeInterface + { + return $this->currentClock; + } + + public function getFactory(): Factory + { + if ($this->currentClock instanceof Factory) { + return $this->currentClock; + } + + if ($this->currentClock instanceof DateTime) { + $factory = new Factory(); + $factory->setTestNowAndTimezone($this->currentClock); + + return $factory; + } + + if ($this->currentClock instanceof DateTimeImmutable) { + $factory = new FactoryImmutable(); + $factory->setTestNowAndTimezone($this->currentClock); + + return $factory; + } + + $factory = new FactoryImmutable(); + $factory->setTestNowAndTimezone(fn () => $this->currentClock->now()); + + return $factory; + } + + private function nowRaw(): DateTimeInterface + { + if ($this->currentClock instanceof DateTimeInterface) { + return $this->currentClock; + } + + if ($this->currentClock instanceof Factory) { + return $this->currentClock->__call('now', []); + } + + return $this->currentClock->now(); + } + + public function now(): DateTimeImmutable + { + $now = $this->nowRaw(); + + return $now instanceof DateTimeImmutable + ? $now + : new CarbonImmutable($now); + } + + /** + * @template T of CarbonInterface + * + * @param class-string $class + * + * @return T + */ + public function nowAs(string $class, DateTimeZone|string|int|null $timezone = null): CarbonInterface + { + $now = $this->nowRaw(); + $date = $now instanceof $class ? $now : $class::instance($now); + + return $timezone === null ? $date : $date->setTimezone($timezone); + } + + public function nowAsCarbon(DateTimeZone|string|int|null $timezone = null): CarbonInterface + { + $now = $this->nowRaw(); + + return $now instanceof CarbonInterface + ? ($timezone === null ? $now : $now->setTimezone($timezone)) + : $this->dateAsCarbon($now, $timezone); + } + + private function dateAsCarbon(DateTimeInterface $date, DateTimeZone|string|int|null $timezone): CarbonInterface + { + return $date instanceof DateTimeImmutable + ? new CarbonImmutable($date, $timezone) + : new Carbon($date, $timezone); + } + + public function sleep(float|int $seconds): void + { + if ($seconds === 0 || $seconds === 0.0) { + return; + } + + if ($seconds < 0) { + throw new RuntimeException('Expected positive number of seconds, '.$seconds.' given'); + } + + if ($this->currentClock instanceof DateTimeInterface) { + $this->currentClock = $this->addSeconds($this->currentClock, $seconds); + + return; + } + + if ($this->currentClock instanceof ClockInterface) { + $this->currentClock->sleep($seconds); + + return; + } + + $this->currentClock = $this->addSeconds($this->currentClock->now(), $seconds); + } + + public function withTimeZone(DateTimeZone|string $timezone): static + { + if ($this->currentClock instanceof ClockInterface) { + return new self($this->currentClock->withTimeZone($timezone)); + } + + $now = $this->currentClock instanceof DateTimeInterface + ? $this->currentClock + : $this->currentClock->now(); + + if (!($now instanceof DateTimeImmutable)) { + $now = clone $now; + } + + if (\is_string($timezone)) { + $timezone = new DateTimeZone($timezone); + } + + return new self($now->setTimezone($timezone)); + } + + private function addSeconds(DateTimeInterface $date, float|int $seconds): DateTimeInterface + { + $secondsPerHour = CarbonInterface::SECONDS_PER_MINUTE * CarbonInterface::MINUTES_PER_HOUR; + $hours = number_format( + floor($seconds / $secondsPerHour), + thousands_separator: '', + ); + $microseconds = number_format( + ($seconds - $hours * $secondsPerHour) * CarbonInterface::MICROSECONDS_PER_SECOND, + thousands_separator: '', + ); + + if (!($date instanceof DateTimeImmutable)) { + $date = clone $date; + } + + if ($hours !== '0') { + $date = $date->modify("$hours hours"); + } + + if ($microseconds !== '0') { + $date = $date->modify("$microseconds microseconds"); + } + + return $date; + } +} diff --git a/vendor/opis/json-schema/LICENSE b/vendor/opis/json-schema/LICENSE new file mode 100644 index 000000000..2bb9ad240 --- /dev/null +++ b/vendor/opis/json-schema/LICENSE @@ -0,0 +1,176 @@ + 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 \ No newline at end of file diff --git a/vendor/opis/json-schema/NOTICE b/vendor/opis/json-schema/NOTICE new file mode 100644 index 000000000..6ebfe377d --- /dev/null +++ b/vendor/opis/json-schema/NOTICE @@ -0,0 +1,9 @@ +Opis Json Schema +Copyright 2018-2021 Zindex Software + +This product includes software developed at +Zindex Software (http://zindex.software). + +This software was originally developed by Marius Sarca and Sorin Sarca +(Copyright 2017-2018). The copyright info was changed with the permission +of the original authors. diff --git a/vendor/opis/json-schema/README.md b/vendor/opis/json-schema/README.md new file mode 100644 index 000000000..018d09c0b --- /dev/null +++ b/vendor/opis/json-schema/README.md @@ -0,0 +1,66 @@ +Opis JSON Schema +==================== +[![Tests](https://github.com/opis/json-schema/workflows/Tests/badge.svg)](https://github.com/opis/json-schema/actions) +[![Packagist Version](https://img.shields.io/packagist/v/opis/json-schema?label=Version)](https://packagist.org/packages/opis/json-schema) +[![Packagist Downloads](https://img.shields.io/packagist/dt/opis/json-schema?label=Downloads)](https://packagist.org/packages/opis/json-schema) +[![Packagist License](https://img.shields.io/packagist/l/opis/json-schema?color=teal&label=License)](https://packagist.org/packages/opis/json-schema) + +Validate JSON documents +----------- + +**Opis JSON Schema** is a PHP implementation for the [JSON Schema] standard (draft-2020-12, draft-2019-09, draft-07 and draft-06), that +will help you validate all sorts of JSON documents, whether they are configuration files or a set +of data sent to a RESTful API endpoint. + + +**The library's key features:** + +- Supports all keywords from all drafts (draft-2020-12 down to draft-06) +- Support for custom errors inside schema using [`$error` keyword](https://opis.io/json-schema/2.x/errors.html) +- Support for custom PHP filters using [`$filters` keyword](https://docs.opis.io/json-schema/2.x/filters.html) +- Advanced schema reuse using [`$map` keyword](https://docs.opis.io/json-schema/2.x/mappers.html) +- Intuitive schema composition using [slots](https://docs.opis.io/json-schema/2.x/slots.html) +- Support for absolute & relative [json pointers](https://docs.opis.io/json-schema/2.x/pointers.html) +- Support for [URI templates](https://docs.opis.io/json-schema/2.x/uri-template.html) +- Support for [`$data` keyword](https://docs.opis.io/json-schema/2.x/data-keyword.html) +- Support for [casting](https://docs.opis.io/json-schema/2.x/pragma.html#cast) +- Support for custom [formats](https://docs.opis.io/json-schema/2.x/php-format.html) and [media types](https://docs.opis.io/json-schema/2.x/php-media-type.html) + +### Documentation + +The full documentation for this library can be found [here][documentation]. +We provide documentation for both [JSON Schema] standard itself as well as for +the library's own API. + +### License + +**Opis JSON Schema** is licensed under the [Apache License, Version 2.0][apache_license]. + +### Requirements + +* PHP ^7.4 || ^8.0 + +## Installation + +**Opis JSON Schema** is available on [Packagist] and it can be installed from a +command line interface by using [Composer]. + +```bash +composer require opis/json-schema +``` + +Or you could directly reference it into your `composer.json` file as a dependency + +```json +{ + "require": { + "opis/json-schema": "^2.4" + } +} +``` + +[documentation]: https://opis.io/json-schema +[apache_license]: https://www.apache.org/licenses/LICENSE-2.0 "Apache License" +[Packagist]: https://packagist.org/packages/opis/json-schema "Packagist" +[Composer]: https://getcomposer.org "Composer" +[JSON Schema]: http://json-schema.org/ "JSON Schema" diff --git a/vendor/opis/json-schema/SECURITY.md b/vendor/opis/json-schema/SECURITY.md new file mode 100644 index 000000000..90e66c1b7 --- /dev/null +++ b/vendor/opis/json-schema/SECURITY.md @@ -0,0 +1,12 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| 2.x | :white_check_mark: | +| 1.x | :white_check_mark: | + +## Reporting a Vulnerability + +Please send an e-mail to authors specified in the [composer.json](https://github.com/opis/json-schema/blob/master/composer.json) file. diff --git a/vendor/opis/json-schema/autoload.php b/vendor/opis/json-schema/autoload.php new file mode 100644 index 000000000..649fab8c0 --- /dev/null +++ b/vendor/opis/json-schema/autoload.php @@ -0,0 +1,23 @@ + false, + 'allowFormats' => true, + 'allowMappers' => false, + 'allowTemplates' => false, + 'allowGlobals' => false, + 'allowDefaults' => false, + 'allowSlots' => false, + 'allowKeywordValidators' => false, + 'allowPragmas' => false, + 'allowDataKeyword' => false, + 'allowKeywordsAlongsideRef' => false, + 'allowUnevaluated' => true, + 'allowRelativeJsonPointerInRef' => false, + 'allowExclusiveMinMaxAsBool' => false, + 'keepDependenciesKeyword' => false, + 'keepAdditionalItemsKeyword' => false, + ]; + + public function __construct( + ?SchemaLoader $loader = null, + int $max_errors = 1, + bool $stop_at_first_error = true + ) + { + parent::__construct($loader, $max_errors, $stop_at_first_error); + + // Set parser options + $parser = $this->parser(); + foreach (static::COMPLIANT_OPTIONS as $name => $value) { + $parser->setOption($name, $value); + } + } +} diff --git a/vendor/opis/json-schema/src/ContentEncoding.php b/vendor/opis/json-schema/src/ContentEncoding.php new file mode 100644 index 000000000..3c6eb2361 --- /dev/null +++ b/vendor/opis/json-schema/src/ContentEncoding.php @@ -0,0 +1,28 @@ +args = $args; + } + + public function getArgs(): array { + return $this->args; + } +} diff --git a/vendor/opis/json-schema/src/Errors/ErrorContainer.php b/vendor/opis/json-schema/src/Errors/ErrorContainer.php new file mode 100644 index 000000000..5cf10dea5 --- /dev/null +++ b/vendor/opis/json-schema/src/Errors/ErrorContainer.php @@ -0,0 +1,148 @@ +maxErrors = $max_errors; + } + + /** + * @return int + */ + public function maxErrors(): int + { + return $this->maxErrors; + } + + /** + * @param ValidationError $error + * @return ErrorContainer + */ + public function add(ValidationError $error): self + { + $this->errors[] = $error; + return $this; + } + + /** + * @return ValidationError[] + */ + public function all(): array + { + return $this->errors; + } + + /** + * @return ValidationError|null + */ + public function first(): ?ValidationError + { + if (!$this->errors) { + return null; + } + + return reset($this->errors); + } + + /** + * @return bool + */ + public function isFull(): bool + { + return count($this->errors) >= $this->maxErrors; + } + + /** + * @return bool + */ + public function isEmpty(): bool + { + return !$this->errors; + } + + /** + * @inheritDoc + */ + public function count(): int + { + return count($this->errors); + } + + /** + * @inheritDoc + */ + public function current(): ?ValidationError + { + return current($this->errors) ?: null; + } + + /** + * @inheritDoc + */ + #[\ReturnTypeWillChange] + public function next(): ?ValidationError + { + return next($this->errors) ?: null; + } + + /** + * @inheritDoc + */ + public function key(): ?int + { + return key($this->errors); + } + + /** + * @inheritDoc + */ + public function valid(): bool + { + return key($this->errors) !== null; + } + + /** + * @inheritDoc + */ + #[\ReturnTypeWillChange] + public function rewind(): ?ValidationError + { + return reset($this->errors) ?: null; + } +} diff --git a/vendor/opis/json-schema/src/Errors/ErrorFormatter.php b/vendor/opis/json-schema/src/Errors/ErrorFormatter.php new file mode 100644 index 000000000..8a994e0b9 --- /dev/null +++ b/vendor/opis/json-schema/src/Errors/ErrorFormatter.php @@ -0,0 +1,423 @@ +getErrors($error) as $error => $message) { + $key = $key_formatter($error); + + if ($multiple) { + if (!isset($list[$key])) { + $list[$key] = []; + } + $list[$key][] = $formatter($error, $message); + } else { + if (!isset($list[$key])) { + $list[$key] = $formatter($error, $message); + } + } + } + + return $list; + } + + /** + * @param ValidationError|null $error + * @param string $mode One of: flag, basic, detailed or verbose + * @return array + */ + public function formatOutput(?ValidationError $error, string $mode = "flag"): array + { + if ($error === null) { + return ['valid' => true]; + } + + if ($mode === 'flag') { + return ['valid' => false]; + } + + if ($mode === 'basic') { + return [ + 'valid' => false, + 'errors' => $this->formatFlat($error, [$this, 'formatOutputError']), + ]; + } + + if ($mode === 'detailed' || $mode === 'verbose') { + $isVerbose = $mode === 'verbose'; + + return $this->getNestedErrors($error, function (ValidationError $error, ?array $subErrors = null) use ($isVerbose) { + $info = $this->formatOutputError($error); + + $info['valid'] = false; + + if ($isVerbose) { + $id = $error->schema()->info(); + $id = $id->root() ?? $id->id(); + if ($id) { + $id = rtrim($id, '#'); + } + $info['absoluteKeywordLocation'] = $id . $info['keywordLocation']; + } + + if ($subErrors) { + $info['errors'] = $subErrors; + if (!$isVerbose) { + unset($info['error']); + } + } + + return $info; + } + ); + } + + return ['valid' => false]; + } + + /** + * @param ValidationError $error + * @param ?callable(ValidationError,?array):mixed $formatter + * @return mixed + */ + public function formatNested(ValidationError $error, ?callable $formatter = null) + { + if (!$formatter) { + $formatter = function (ValidationError $error, ?array $subErrors = null): array { + $ret = [ + 'message' => $this->formatErrorMessage($error), + 'keyword' => $error->keyword(), + 'path' => $this->formatErrorKey($error), + ]; + + if ($subErrors) { + $ret['errors'] = $subErrors; + } + + return $ret; + }; + } + + return $this->getNestedErrors($error, $formatter); + } + + /** + * @param ValidationError $error + * @param ?callable(ValidationError):mixed $formatter + * @return array + */ + public function formatFlat(ValidationError $error, ?callable $formatter = null): array + { + if (!$formatter) { + $formatter = [$this, 'formatErrorMessage']; + } + + $list = []; + + foreach ($this->getFlatErrors($error) as $error) { + $list[] = $formatter($error); + } + + return $list; + } + + /** + * @param ValidationError $error + * @param ?callable(ValidationError):mixed $formatter + * @param ?callable(ValidationError):string $key_formatter + * @return array + */ + public function formatKeyed( + ValidationError $error, + ?callable $formatter = null, + ?callable $key_formatter = null + ): array { + if (!$formatter) { + $formatter = [$this, 'formatErrorMessage']; + } + + if (!$key_formatter) { + $key_formatter = [$this, 'formatErrorKey']; + } + + $list = []; + + foreach ($this->getLeafErrors($error) as $error) { + $key = $key_formatter($error); + + if (!isset($list[$key])) { + $list[$key] = []; + } + + $list[$key][] = $formatter($error); + } + + return $list; + } + + /** + * @param ValidationError $error + * @param string|null $message The message to use, if null $error->message() is used + * @return string + */ + public function formatErrorMessage(ValidationError $error, ?string $message = null): string + { + $message ??= $error->message(); + $args = $this->getDefaultArgs($error) + $error->args(); + + if (!$args) { + return $message; + } + + return preg_replace_callback( + '~{([^}]+)}~imu', + static function (array $m) use ($args) { + if (!array_key_exists($m[1], $args)) { + return $m[0]; + } + + $value = $args[$m[1]]; + + if (is_array($value)) { + return implode(', ', $value); + } + + return (string) $value; + }, + $message + ); + } + + public function formatErrorKey(ValidationError $error): string + { + return JsonPointer::pathToString($error->data()->fullPath()); + } + + protected function getDefaultArgs(ValidationError $error): array + { + $data = $error->data(); + $info = $error->schema()->info(); + + $path = $info->path(); + $path[] = $error->keyword(); + + return [ + 'data:type' => $data->type(), + 'data:value' => $data->value(), + 'data:path' => JsonPointer::pathToString($data->fullPath()), + + 'schema:id' => $info->id(), + 'schema:root' => $info->root(), + 'schema:base' => $info->base(), + 'schema:draft' => $info->draft(), + 'schema:keyword' => $error->keyword(), + 'schema:path' => JsonPointer::pathToString($path), + ]; + } + + protected function formatOutputError(ValidationError $error): array + { + $path = $error->schema()->info()->path(); + + $path[] = $error->keyword(); + + return [ + 'keywordLocation' => JsonPointer::pathToFragment($path), + 'instanceLocation' => JsonPointer::pathToFragment($error->data()->fullPath()), + 'error' => $this->formatErrorMessage($error), + ]; + } + + /** + * @param ValidationError $error + * @param callable(ValidationError,?array):mixed $formatter + * @return mixed + */ + protected function getNestedErrors(ValidationError $error, callable $formatter) + { + if ($subErrors = $error->subErrors()) { + foreach ($subErrors as &$subError) { + $subError = $this->getNestedErrors($subError, $formatter); + unset($subError); + } + } + + return $formatter($error, $subErrors); + } + + /** + * @param ValidationError $error + * @return iterable|ValidationError[] + */ + protected function getFlatErrors(ValidationError $error): iterable + { + yield $error; + + foreach ($error->subErrors() as $subError) { + yield from $this->getFlatErrors($subError); + } + } + + /** + * @param ValidationError $error + * @return iterable|ValidationError[] + */ + protected function getLeafErrors(ValidationError $error): iterable + { + if ($subErrors = $error->subErrors()) { + foreach ($subErrors as $subError) { + yield from $this->getLeafErrors($subError); + } + } else { + yield $error; + } + } + + /** + * @param ValidationError $error + * @return iterable + */ + protected function getErrors(ValidationError $error): iterable + { + $data = $error->schema()->info()->data(); + + $map = null; + $pMap = null; + + if (is_object($data)) { + switch ($error->keyword()) { + case 'required': + if (isset($data->{'$error'}->required) && is_object($data->{'$error'}->required)) { + $e = $data->{'$error'}->required; + $found = false; + foreach ($error->args()['missing'] as $prop) { + if (isset($e->{$prop})) { + yield $error => $e->{$prop}; + $found = true; + } + } + if ($found) { + return; + } + if (isset($e->{'*'})) { + yield $error => $e->{'*'}; + return; + } + unset($e, $found, $prop); + } + break; + case '$filters': + if (($args = $error->args()) && isset($args['args']['$error'])) { + yield $error => $args['args']['$error']; + return; + } + unset($args); + break; + } + + if (isset($data->{'$error'})) { + $map = $data->{'$error'}; + + if (is_string($map)) { + // We have an global error + yield $error => $map; + return; + } + + if (is_object($map)) { + if (isset($map->{$error->keyword()})) { + $pMap = $map->{'*'} ?? null; + $map = $map->{$error->keyword()}; + if (is_string($map)) { + yield $error => $map; + return; + } + } elseif (isset($map->{'*'})) { + yield $error => $map->{'*'}; + return; + } + } + } + } + + if (!is_object($map)) { + $map = null; + } + + $subErrors = $error->subErrors(); + + if (!$subErrors) { + yield $error => $pMap ?? $error->message(); + return; + } + + if (!$map) { + foreach ($subErrors as $subError) { + yield from $this->getErrors($subError); + } + return; + } + + foreach ($subErrors as $subError) { + $path = $subError->data()->path(); + if (count($path) !== 1) { + yield from $this->getErrors($subError); + } else { + $path = $path[0]; + if (isset($map->{$path})) { + yield $subError => $map->{$path}; + } elseif (isset($map->{'*'})) { + yield $subError => $map->{'*'}; + } else { + yield from $this->getErrors($subError); + } + } + } + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Errors/ValidationError.php b/vendor/opis/json-schema/src/Errors/ValidationError.php new file mode 100644 index 000000000..f7c11a4fe --- /dev/null +++ b/vendor/opis/json-schema/src/Errors/ValidationError.php @@ -0,0 +1,96 @@ +keyword = $keyword; + $this->schema = $schema; + $this->data = $data; + $this->message = $message; + $this->args = $args; + $this->subErrors = $subErrors; + } + + public function keyword(): string + { + return $this->keyword; + } + + public function schema(): Schema + { + return $this->schema; + } + + public function data(): DataInfo + { + return $this->data; + } + + public function args(): array + { + return $this->args; + } + + public function message(): string + { + return $this->message; + } + + public function subErrors(): array + { + return $this->subErrors; + } + + public function __toString(): string + { + return $this->message; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Exceptions/DuplicateSchemaIdException.php b/vendor/opis/json-schema/src/Exceptions/DuplicateSchemaIdException.php new file mode 100644 index 000000000..b943ec8ed --- /dev/null +++ b/vendor/opis/json-schema/src/Exceptions/DuplicateSchemaIdException.php @@ -0,0 +1,57 @@ +id = $id; + $this->data = $data; + } + + /** + * @return null|object + */ + public function getData(): ?object + { + return $this->data; + } + + /** + * @return Uri + */ + public function getId(): Uri + { + return $this->id; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Exceptions/InvalidKeywordException.php b/vendor/opis/json-schema/src/Exceptions/InvalidKeywordException.php new file mode 100644 index 000000000..33a340974 --- /dev/null +++ b/vendor/opis/json-schema/src/Exceptions/InvalidKeywordException.php @@ -0,0 +1,46 @@ +keyword = $keyword; + } + + /** + * @return string + */ + public function keyword(): string + { + return $this->keyword; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Exceptions/InvalidPragmaException.php b/vendor/opis/json-schema/src/Exceptions/InvalidPragmaException.php new file mode 100644 index 000000000..d3ff2043b --- /dev/null +++ b/vendor/opis/json-schema/src/Exceptions/InvalidPragmaException.php @@ -0,0 +1,46 @@ +pragma = $pragma; + } + + /** + * @return string + */ + public function pragma(): string + { + return $this->pragma; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Exceptions/ParseException.php b/vendor/opis/json-schema/src/Exceptions/ParseException.php new file mode 100644 index 000000000..23e84f9ee --- /dev/null +++ b/vendor/opis/json-schema/src/Exceptions/ParseException.php @@ -0,0 +1,45 @@ +info = $info; + } + + /** + * @return SchemaInfo|null + */ + public function schemaInfo(): ?SchemaInfo + { + return $this->info; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Exceptions/SchemaException.php b/vendor/opis/json-schema/src/Exceptions/SchemaException.php new file mode 100644 index 000000000..feedbcd4c --- /dev/null +++ b/vendor/opis/json-schema/src/Exceptions/SchemaException.php @@ -0,0 +1,25 @@ +encoding = $encoding; + } + + /** + * @return string + */ + public function getContentEncoding(): string + { + return $this->encoding; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Exceptions/UnresolvedContentMediaTypeException.php b/vendor/opis/json-schema/src/Exceptions/UnresolvedContentMediaTypeException.php new file mode 100644 index 000000000..ef83d98ec --- /dev/null +++ b/vendor/opis/json-schema/src/Exceptions/UnresolvedContentMediaTypeException.php @@ -0,0 +1,44 @@ +media = $media; + } + + /** + * @return string + */ + public function getContentMediaType(): string + { + return $this->media; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Exceptions/UnresolvedException.php b/vendor/opis/json-schema/src/Exceptions/UnresolvedException.php new file mode 100644 index 000000000..fb876cf67 --- /dev/null +++ b/vendor/opis/json-schema/src/Exceptions/UnresolvedException.php @@ -0,0 +1,57 @@ +schema = $schema; + $this->context = $context; + } + + /** + * @return Schema + */ + public function getSchema(): Schema + { + return $this->schema; + } + + /** + * @return ValidationContext + */ + public function getContext(): ValidationContext + { + return $this->context; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Exceptions/UnresolvedFilterException.php b/vendor/opis/json-schema/src/Exceptions/UnresolvedFilterException.php new file mode 100644 index 000000000..180693e42 --- /dev/null +++ b/vendor/opis/json-schema/src/Exceptions/UnresolvedFilterException.php @@ -0,0 +1,57 @@ +filter = $filter; + $this->type = $type; + } + + /** + * @return string + */ + public function getFilter(): string + { + return $this->filter; + } + + /** + * @return string + */ + public function getType(): string + { + return $this->type; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Exceptions/UnresolvedReferenceException.php b/vendor/opis/json-schema/src/Exceptions/UnresolvedReferenceException.php new file mode 100644 index 000000000..5c48bed66 --- /dev/null +++ b/vendor/opis/json-schema/src/Exceptions/UnresolvedReferenceException.php @@ -0,0 +1,45 @@ +ref = $ref; + } + + /** + * @return string + */ + public function getRef(): string + { + return $this->ref; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Filter.php b/vendor/opis/json-schema/src/Filter.php new file mode 100644 index 000000000..1e1b002f6 --- /dev/null +++ b/vendor/opis/json-schema/src/Filter.php @@ -0,0 +1,29 @@ +currentData(); + if (!is_string($ref)) { + return false; + } + + $ref = JsonPointer::parse($ref); + if ($ref === null) { + return false; + } + + return $ref->data($context->rootData(), $context->currentDataPath(), $this) !== $this; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Filters/DateTimeFilters.php b/vendor/opis/json-schema/src/Filters/DateTimeFilters.php new file mode 100644 index 000000000..a43eaaacd --- /dev/null +++ b/vendor/opis/json-schema/src/Filters/DateTimeFilters.php @@ -0,0 +1,100 @@ += self::CreateDate($min, $tz, false); + } + + public static function MaxDate(string $date, array $args): bool + { + $max = $args['value']; + $tz = $args['timezone'] ?? null; + + return self::CreateDate($date, $tz, false) <= self::CreateDate($max, $tz, false); + } + + public static function NotDate(string $date, array $args): bool + { + $not = $args['value']; + $tz = $args['timezone'] ?? null; + + if (!is_array($not)) { + $not = [$not]; + } + + $date = self::CreateDate($date, $tz, false); + + foreach ($not as $d) { + if ($date == self::CreateDate($d, $tz, false)) { + return false; + } + } + + return true; + } + + public static function MinDateTime(string $date, array $args): bool + { + $min = $args['value']; + $tz = $args['timezone'] ?? null; + + return self::CreateDate($date, $tz) >= self::CreateDate($min, $tz); + } + + public static function MaxDateTime(string $date, array $args): bool + { + $max = $args['value']; + $tz = $args['timezone'] ?? null; + + return self::CreateDate($date, $tz) <= self::CreateDate($max, $tz); + } + + public static function MinTime(string $time, array $args): bool + { + $min = $args['value']; + $prefix = '1970-01-01 '; + + return self::CreateDate($prefix . $time) >= self::CreateDate($prefix . $min); + } + + public static function MaxTime(string $time, array $args): bool + { + $max = $args['value']; + $prefix = '1970-01-01 '; + + return self::CreateDate($prefix . $time) <= self::CreateDate($prefix . $max); + } + + private static function CreateDate(string $value, ?string $timezone = null, bool $time = true): DateTime + { + $date = new DateTime($value, $timezone ? new DateTimeZone($timezone) : null); + if (!$time) { + return $date->setTime(0, 0, 0, 0); + } + return $date; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Filters/FilterExistsFilter.php b/vendor/opis/json-schema/src/Filters/FilterExistsFilter.php new file mode 100644 index 000000000..3b92ade3b --- /dev/null +++ b/vendor/opis/json-schema/src/Filters/FilterExistsFilter.php @@ -0,0 +1,54 @@ +currentData(); + if (!is_string($filter)) { + return false; + } + + $type = null; + if (isset($args['type'])) { + if (!is_string($args['type'])) { + return false; + } + $type = $args['type']; + } + + $resolver = $context->loader()->parser()->getFilterResolver(); + + if (!$resolver) { + return false; + } + + if ($type === null) { + return (bool)$resolver->resolveAll($filter); + } + + return (bool)$resolver->resolve($filter, $type); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Filters/FormatExistsFilter.php b/vendor/opis/json-schema/src/Filters/FormatExistsFilter.php new file mode 100644 index 000000000..dacd482f1 --- /dev/null +++ b/vendor/opis/json-schema/src/Filters/FormatExistsFilter.php @@ -0,0 +1,54 @@ +currentData(); + if (!is_string($format)) { + return false; + } + + $type = null; + if (isset($args['type'])) { + if (!is_string($args['type'])) { + return false; + } + $type = $args['type']; + } + + $resolver = $context->loader()->parser()->getFormatResolver(); + + if (!$resolver) { + return false; + } + + if ($type === null) { + return (bool)$resolver->resolveAll($format); + } + + return (bool)$resolver->resolve($format, $type); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Filters/GlobalVarExistsFilter.php b/vendor/opis/json-schema/src/Filters/GlobalVarExistsFilter.php new file mode 100644 index 000000000..44aa39630 --- /dev/null +++ b/vendor/opis/json-schema/src/Filters/GlobalVarExistsFilter.php @@ -0,0 +1,47 @@ +currentData(); + + if (!is_string($var)) { + return false; + } + + $globals = $context->globals(); + + if (!array_key_exists($var, $globals)) { + return false; + } + + if (array_key_exists('value', $args)) { + return $globals[$var] == $args['value']; + } + + return true; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Filters/SchemaExistsFilter.php b/vendor/opis/json-schema/src/Filters/SchemaExistsFilter.php new file mode 100644 index 000000000..4570bcb8d --- /dev/null +++ b/vendor/opis/json-schema/src/Filters/SchemaExistsFilter.php @@ -0,0 +1,84 @@ +currentData(); + if (!is_string($ref)) { + return false; + } + + if (UriTemplate::isTemplate($ref)) { + if (isset($args['vars']) && is_object($args['vars'])) { + $vars = new VariablesContainer($args['vars'], false); + $vars = $vars->resolve($context->rootData(), $context->currentDataPath()); + if (!is_array($vars)) { + $vars = (array)$vars; + } + $vars += $context->globals(); + } else { + $vars = $context->globals(); + } + + $ref = (new UriTemplate($ref))->resolve($vars); + + unset($vars); + } + + unset($args); + + return $this->refExists($ref, $context, $schema); + } + + /** + * @param string $ref + * @param ValidationContext $context + * @param Schema $schema + * @return bool + */ + protected function refExists(string $ref, ValidationContext $context, Schema $schema): bool + { + if ($ref === '') { + return false; + } + + if ($ref === '#') { + return true; + } + + $info = $schema->info(); + + $id = Uri::merge($ref, $info->idBaseRoot(), true); + + if ($id === null) { + return false; + } + + return $context->loader()->loadSchemaById($id) !== null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Filters/SlotExistsFilter.php b/vendor/opis/json-schema/src/Filters/SlotExistsFilter.php new file mode 100644 index 000000000..b703bab06 --- /dev/null +++ b/vendor/opis/json-schema/src/Filters/SlotExistsFilter.php @@ -0,0 +1,36 @@ +currentData(); + if (!is_string($slot)) { + return false; + } + + return $context->slot($slot) !== null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Format.php b/vendor/opis/json-schema/src/Format.php new file mode 100644 index 000000000..577a53495 --- /dev/null +++ b/vendor/opis/json-schema/src/Format.php @@ -0,0 +1,27 @@ +.+)@(?.+)$/u', $value, $m)) { + return false; + } + + $m['name'] = $idn($m['name']); + if ($m['name'] === null) { + return false; + } + + $m['domain'] = $idn($m['domain']); + if ($m['domain'] === null) { + return false; + } + + $value = $m['name'] . '@' . $m['domain']; + } + + return filter_var($value, FILTER_VALIDATE_EMAIL) !== false; + } + + /** + * @return callable|null + */ + public static function idn(): ?callable + { + if (static::$idn === false) { + if (function_exists('idn_to_ascii')) { + static::$idn = static function (string $value): ?string { + /** @noinspection PhpComposerExtensionStubsInspection */ + $value = idn_to_ascii($value, 0, INTL_IDNA_VARIANT_UTS46); + + return is_string($value) ? $value : null; + }; + } else { + static::$idn = null; + } + } + + return static::$idn; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Formats/MiscFormats.php b/vendor/opis/json-schema/src/Formats/MiscFormats.php new file mode 100644 index 000000000..a7e2af5fe --- /dev/null +++ b/vendor/opis/json-schema/src/Formats/MiscFormats.php @@ -0,0 +1,59 @@ +isAbsolute(); + } + + /** + * @param string $value + * @return bool + */ + public static function uriReference(string $value): bool + { + if ($value === '') { + return true; + } + + return Uri::parse($value) !== null; + } + + /** + * @param string $value + * @return bool + */ + public static function uriTemplate(string $value): bool + { + if ($value === '') { + return true; + } + + if (UriTemplate::isTemplate($value)) { + return true; + } + + return Uri::parse($value) !== null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Helper.php b/vendor/opis/json-schema/src/Helper.php new file mode 100644 index 000000000..45bdf4042 --- /dev/null +++ b/vendor/opis/json-schema/src/Helper.php @@ -0,0 +1,392 @@ + 'number']; + + /** @var string[] */ + protected const PHP_TYPE_MAP = [ + 'NULL' => 'null', + 'integer' => 'integer', + 'double' => 'number', + 'boolean' => 'boolean', + 'array' => 'array', + 'object' => 'object', + 'string' => 'string', + ]; + + /** + * @param string $type + * @return bool + */ + public static function isValidJsonType(string $type): bool + { + if (isset(self::JSON_SUBTYPES[$type])) { + return true; + } + + return in_array($type, self::JSON_TYPES, true); + } + + /** + * @param string $type + * @return null|string + */ + public static function getJsonSuperType(string $type): ?string + { + return self::JSON_SUBTYPES[$type] ?? null; + } + + /** + * @param mixed $value + * @param bool $use_subtypes + * @return null|string + */ + public static function getJsonType($value, bool $use_subtypes = true): ?string + { + $type = self::PHP_TYPE_MAP[gettype($value)] ?? null; + if ($type === null) { + return null; + } elseif ($type === 'array') { + return self::isIndexedArray($value) ? 'array' : null; + } + + if ($use_subtypes) { + if ($type === 'number' && self::isMultipleOf($value, 1)) { + return 'integer'; + } + } elseif ($type === 'integer') { + return 'number'; + } + + return $type; + } + + /** + * @param string $type + * @param string|string[] $allowed + * @return bool + */ + public static function jsonTypeMatches(string $type, $allowed): bool + { + if (!$allowed) { + return false; + } + + if (is_string($allowed)) { + if ($type === $allowed) { + return true; + } + + return $allowed === self::getJsonSuperType($type); + } + + if (is_array($allowed)) { + if (in_array($type, $allowed, true)) { + return true; + } + + if ($type = self::getJsonSuperType($type)) { + return in_array($type, $allowed, true); + } + } + + return false; + } + + /** + * @param mixed $value + * @param string|string[] $type + * @return bool + */ + public static function valueIsOfJsonType($value, $type): bool + { + $t = self::getJsonType($value); + if ($t === null) { + return false; + } + + return self::jsonTypeMatches($t, $type); + } + + /** + * @param array $array + * @return bool + */ + public static function isIndexedArray(array $array): bool + { + for ($i = 0, $max = count($array); $i < $max; $i++) { + if (!array_key_exists($i, $array)) { + return false; + } + } + + return true; + } + + /** + * Converts assoc-arrays to objects (recursive) + * @param scalar|object|array|null $schema + * @return scalar|object|array|null + */ + public static function convertAssocArrayToObject($schema) + { + if (is_null($schema) || is_scalar($schema)) { + return $schema; + } + + $keepArray = is_array($schema) && self::isIndexedArray($schema); + + $data = []; + + foreach ($schema as $key => $value) { + $data[$key] = is_array($value) || is_object($value) ? self::convertAssocArrayToObject($value) : $value; + } + + return $keepArray ? $data : (object) $data; + } + + /** + * @param mixed $a + * @param mixed $b + * @return bool + */ + public static function equals($a, $b): bool + { + if ($a === $b) { + return true; + } + + $type = self::getJsonType($a, false); + if ($type === null || $type !== self::getJsonType($b, false)) { + return false; + } + + if ($type === 'number') { + return $a == $b; + } + + if ($type === "array") { + $count = count($a); + if ($count !== count($b)) { + return false; + } + + for ($i = 0; $i < $count; $i++) { + if (!array_key_exists($i, $a) || !array_key_exists($i, $b)) { + return false; + } + if (!self::equals($a[$i], $b[$i])) { + return false; + } + } + + return true; + } + + if ($type === "object") { + $a = get_object_vars($a); + if ($a === null) { + return false; + } + + $b = get_object_vars($b); + if ($b === null) { + return false; + } + + if (count($a) !== count($b)) { + return false; + } + + foreach ($a as $prop => $value) { + if (!array_key_exists($prop, $b)) { + return false; + } + if (!self::equals($value, $b[$prop])) { + return false; + } + } + + return true; + } + + return false; + } + + + /** + * @var bool|null True if bcmath extension is available + */ + private static ?bool $hasBCMath = null; + + /** + * @var bool True to use bcmath + */ + public static bool $useBCMath = true; + + /** + * @var int Number scale to used when using comparisons + */ + public static int $numberScale = 14; + + /** + * @param $number + * @param $divisor + * @param int|null $scale + * @return bool + */ + public static function isMultipleOf($number, $divisor, ?int $scale = null): bool + { + if (!$number || $number == $divisor) { + return true; + } + + if ($divisor == 0) { + return $number == 0; + } + + // maybe we get lucky + if (!fmod($number, $divisor)) { + return true; + } + + $divisor = abs($divisor); + if ($divisor == 1) { + // should have been caught by the above fmod() + return false; + } + + $number = abs($number); + if ($divisor > $number) { + // number / divisor < 1 + return false; + } + + // Use global scale if null + $scale ??= self::$numberScale; + + if ( + !self::$useBCMath || + !(self::$hasBCMath ??= extension_loaded('bcmath')) + ) { + // use an approximation + return abs($number - round($number / $divisor) * $divisor) < (10 ** -$scale); + } + + // use bcmath + + $number = number_format($number, $scale, '.', ''); + $divisor = number_format($divisor, $scale, '.', ''); + + // number can be zero after formatting + if (!(float)$divisor) { + return $number === $divisor; + } + + $x = bcdiv($number, $divisor, 0); + $x = bcmul($divisor, $x, $scale); + $x = bcsub($number, $x, $scale); + + return 0 === bccomp($x, 0, $scale); + } + + /** + * @param $value + * @return mixed + */ + public static function cloneValue($value) + { + if ($value === null || is_scalar($value)) { + return $value; + } + + if (is_array($value)) { + return array_map(self::class . '::cloneValue', $value); + } + + if (is_object($value)) { + return (object)array_map(self::class . '::cloneValue', get_object_vars($value)); + } + + return null; + } + + /** + * @param string $pattern + * @return bool + */ + public static function isValidPattern(string $pattern): bool + { + if (strpos($pattern, '\Z') !== false) { + return false; + } + + return @preg_match("\x07{$pattern}\x07u", '') !== false; + } + + /** + * @param string $pattern + * @return string + */ + public static function patternToRegex(string $pattern): string + { + return "\x07{$pattern}\x07uD"; + } + + /** + * @param mixed $data + * @return mixed + */ + public static function toJSON($data) + { + if ($data === null || is_scalar($data)) { + return $data; + } + + $map = []; + + $isArray = true; + $index = 0; + foreach ($data as $key => $value) { + $map[$key] = self::toJSON($value); + if ($isArray) { + if ($index !== $key) { + $isArray = false; + } else { + $index++; + } + } + } + + if ($isArray) { + if (!$map && is_object($data)) { + return (object) $map; + } + return $map; + } + + return (object) $map; + } +} diff --git a/vendor/opis/json-schema/src/Info/DataInfo.php b/vendor/opis/json-schema/src/Info/DataInfo.php new file mode 100644 index 000000000..57dc8a74a --- /dev/null +++ b/vendor/opis/json-schema/src/Info/DataInfo.php @@ -0,0 +1,114 @@ +value = $value; + $this->type = $type; + $this->root = $root; + $this->path = $path; + $this->parent = $parent; + } + + public function value() + { + return $this->value; + } + + public function type(): ?string + { + return $this->type; + } + + public function root() + { + return $this->root; + } + + /** + * @return int[]|string[] + */ + public function path(): array + { + return $this->path; + } + + public function parent(): ?DataInfo + { + return $this->parent; + } + + /** + * @return int[]|string[] + */ + public function fullPath(): array + { + if ($this->parent === null) { + return $this->path; + } + + if ($this->fullPath === null) { + $this->fullPath = array_merge($this->parent->fullPath(), $this->path); + } + + return $this->fullPath; + } + + /** + * @param ValidationContext $context + * @return static + */ + public static function fromContext(ValidationContext $context): self + { + if ($parent = $context->parent()) { + $parent = self::fromContext($parent); + } + + return new self($context->currentData(), $context->currentDataType(), $context->rootData(), + $context->currentDataPath(), $parent); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Info/SchemaInfo.php b/vendor/opis/json-schema/src/Info/SchemaInfo.php new file mode 100644 index 000000000..77b2d05d8 --- /dev/null +++ b/vendor/opis/json-schema/src/Info/SchemaInfo.php @@ -0,0 +1,117 @@ +data = $data; + $this->id = $id; + $this->root = $root; + $this->base = $base; + $this->path = $path; + $this->draft = $draft; + } + + public function id(): ?Uri + { + return $this->id; + } + + public function root(): ?Uri + { + return $this->root; + } + + public function base(): ?Uri + { + return $this->base; + } + + public function draft(): ?string + { + return $this->draft; + } + + public function data() + { + return $this->data; + } + + public function path(): array + { + return $this->path; + } + + /** + * Returns first non-null property: id, base or root + * @return Uri|null + */ + public function idBaseRoot(): ?Uri + { + return $this->id ?? $this->base ?? $this->root; + } + + public function isBoolean(): bool + { + return is_bool($this->data); + } + + public function isObject(): bool + { + return is_object($this->data); + } + + public function isDocumentRoot(): bool + { + return $this->id && !$this->root && !$this->base; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/JsonPointer.php b/vendor/opis/json-schema/src/JsonPointer.php new file mode 100644 index 000000000..22a6127e3 --- /dev/null +++ b/vendor/opis/json-schema/src/JsonPointer.php @@ -0,0 +1,410 @@ +0|[1-9][0-9]*)(?(?:\+|-)(?:0|[1-9][0-9]*))?)?(?(?:/[^/#]*)*)(?#)?$~'; + + /** @var string */ + protected const UNESCAPED = '/~([^01]|$)/'; + + protected int $level = -1; + + protected int $shift = 0; + + protected bool $fragment = false; + + /** @var string[]|int[] */ + protected array $path; + + protected ?string $str = null; + + final protected function __construct(array $path, int $level = -1, int $shift = 0, bool $fragment = false) + { + $this->path = $path; + $this->level = $level < 0 ? -1 : $level; + $this->shift = $shift; + $this->fragment = $level >= 0 && $fragment; + } + + public function isRelative(): bool + { + return $this->level >= 0; + } + + public function isAbsolute(): bool + { + return $this->level < 0; + } + + public function level(): int + { + return $this->level; + } + + public function shift(): int + { + return $this->shift; + } + + /** + * @return string[] + */ + public function path(): array + { + return $this->path; + } + + /** + * @return bool + */ + public function hasFragment(): bool + { + return $this->fragment; + } + + /** + * @return string + */ + public function __toString(): string + { + if ($this->str === null) { + if ($this->level >= 0) { + $this->str = (string)$this->level; + + if ($this->shift !== 0) { + if ($this->shift > 0) { + $this->str .= '+'; + } + $this->str .= $this->shift; + } + + if ($this->path) { + $this->str .= '/'; + $this->str .= implode('/', self::encodePath($this->path)); + } + + if ($this->fragment) { + $this->str .= '#'; + } + } else { + $this->str = '/'; + $this->str .= implode('/', self::encodePath($this->path)); + } + } + + return $this->str; + } + + /** + * @param $data + * @param array|null $path + * @param mixed $default + * @return mixed + */ + public function data($data, ?array $path = null, $default = null) + { + if ($this->level < 0) { + return self::getData($data, $this->path, false, $default); + } + + if ($path !== null) { + $path = $this->absolutePath($path); + } + + if ($path === null) { + return $default; + } + + return self::getData($data, $path, $this->fragment, $default); + } + + /** + * @param array $path + * @return array|null + */ + public function absolutePath(array $path = []): ?array + { + if ($this->level < 0) { + // Absolute pointer + return $this->path; + } + + if ($this->level === 0) { + if ($this->shift && !$this->handleShift($path)) { + return null; + } + return $this->path ? array_merge($path, $this->path) : $path; + } + + $count = count($path); + if ($count === $this->level) { + if ($this->shift) { + return null; + } + return $this->path; + } + + if ($count > $this->level) { + $count -= $this->level; + + /** @var array $path */ + $path = array_slice($path, 0, $count); + + if ($this->shift && !$this->handleShift($path, $count)) { + return null; + } + + return $this->path ? array_merge($path, $this->path) : $path; + } + + return null; + } + + protected function handleShift(array &$path, ?int $count = null): bool + { + if (!$path) { + return false; + } + + $count ??= count($path); + + $last = $path[$count - 1]; + + if (is_string($last) && preg_match('/^[1-9]\d*$/', $last)) { + $last = (int) $last; + } + + if (!is_int($last)) { + return false; + } + + $path[$count - 1] = $last + $this->shift; + + return true; + } + + public static function parse(string $pointer, bool $decode = true): ?self + { + if ($pointer === '' || !preg_match(self::PATTERN, $pointer, $m)) { + // Not a pointer + return null; + } + + $pointer = $m['pointer'] ?? null; + + // Check if the pointer is escaped + if ($decode && $pointer && preg_match(self::UNESCAPED, $pointer)) { + // Invalid pointer + return null; + } + + $level = isset($m['level']) && $m['level'] !== '' + ? (int)$m['level'] + : -1; + + $shift = 0; + if ($level >= 0 && isset($m['shift']) && $m['shift'] !== '') { + $shift = (int) $m['shift']; + } + + $fragment = isset($m['fragment']) && $m['fragment'] === '#'; + unset($m); + + if ($fragment && $level < 0) { + return null; + } + + if ($pointer === '') { + $pointer = null; + } elseif ($pointer !== null) { + // Remove leading slash + $pointer = substr($pointer, 1); + + if ($pointer !== '') { + $pointer = self::decodePath(explode('/', $pointer)); + } else { + $pointer = null; + } + } + + return new self($pointer ?? [], $level, $shift, $fragment); + } + + /** + * @param $data + * @param array|null $path + * @param bool $fragment + * @param mixed $default + * @return mixed + */ + public static function getData($data, ?array $path = null, bool $fragment = false, $default = null) + { + if ($path === null) { + return $default; + } + + if (!$path) { + return $fragment ? $default : $data; + } + + if ($fragment) { + return end($path); + } + + foreach ($path as $key) { + if (is_array($data)) { + if (!array_key_exists($key, $data)) { + return $default; + } + $data = $data[$key]; + } elseif (is_object($data)) { + if (!property_exists($data, $key)) { + return $default; + } + $data = $data->{$key}; + } else { + return $default; + } + } + + return $data; + } + + /** + * @param string|string[] $path + * @return string|string[] + */ + public static function encodePath($path) + { + $path = str_replace('~', '~0', $path); + $path = str_replace('/', '~1', $path); + + if (is_array($path)) { + return array_map('rawurlencode', $path); + } + + return rawurlencode($path); + } + + /** + * @param string|string[] $path + * @return string|string[] + */ + public static function decodePath($path) + { + if (is_array($path)) { + $path = array_map('rawurldecode', $path); + } else { + $path = rawurldecode($path); + } + + $path = str_replace('~1', '/', $path); + $path = str_replace('~0', '~', $path); + + return $path; + } + + /** + * @param array $path + * @return string + */ + public static function pathToString(array $path): string + { + if (!$path) { + return '/'; + } + + return '/' . implode('/', self::encodePath($path)); + } + + /** + * @param array $path + * @return string + */ + public static function pathToFragment(array $path): string + { + if (!$path) { + return '#'; + } + + return '#/' . implode('/', self::encodePath($path)); + } + + /** + * @param string $pointer + * @return bool + */ + public static function isAbsolutePointer(string $pointer): bool + { + if ($pointer === '/') { + return true; + } + + if (!preg_match(self::PATTERN, $pointer, $m)) { + return false; + } + + if (isset($m['fragment']) || isset($m['level']) && $m['level'] !== '') { + return false; + } + + if (!isset($m['pointer']) || $m['pointer'] === '') { + return true; + } + + return !preg_match(self::UNESCAPED, $m['pointer']); + } + + /** + * @param string $pointer + * @return bool + */ + public static function isRelativePointer(string $pointer): bool + { + if ($pointer === '') { + return false; + } + + if (!preg_match(self::PATTERN, $pointer, $m)) { + return false; + } + + if (!isset($m['level']) || $m['level'] === '' || (int)$m['level'] < 0) { + return false; + } + + if (!isset($m['pointer']) || $m['pointer'] === '') { + return true; + } + + return !preg_match(self::UNESCAPED, $m['pointer']); + } + + public static function createAbsolute(array $path): self + { + return new self($path, -1, 0, false); + } + + public static function createRelative(int $level, array $path = [], int $shift = 0, bool $fragment = false): self + { + return new self($path, $level, $shift, $fragment); + } +} diff --git a/vendor/opis/json-schema/src/Keyword.php b/vendor/opis/json-schema/src/Keyword.php new file mode 100644 index 000000000..d10c90318 --- /dev/null +++ b/vendor/opis/json-schema/src/Keyword.php @@ -0,0 +1,30 @@ +next; + } + + /** + * @inheritDoc + */ + public function setNext(?KeywordValidator $next): KeywordValidator + { + $this->next = $next; + + return $this; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/KeywordValidators/CallbackKeywordValidator.php b/vendor/opis/json-schema/src/KeywordValidators/CallbackKeywordValidator.php new file mode 100644 index 000000000..2cb7f5933 --- /dev/null +++ b/vendor/opis/json-schema/src/KeywordValidators/CallbackKeywordValidator.php @@ -0,0 +1,59 @@ +callback = $callback; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context): ?ValidationError + { + return ($this->callback)($context); + } + + /** + * @inheritDoc + */ + public function next(): ?KeywordValidator + { + return null; + } + + /** + * @inheritDoc + */ + public function setNext(?KeywordValidator $next): KeywordValidator + { + return $this; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/KeywordValidators/PragmaKeywordValidator.php b/vendor/opis/json-schema/src/KeywordValidators/PragmaKeywordValidator.php new file mode 100644 index 000000000..7913b067e --- /dev/null +++ b/vendor/opis/json-schema/src/KeywordValidators/PragmaKeywordValidator.php @@ -0,0 +1,63 @@ +pragmas = $pragmas; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context): ?ValidationError + { + if (!$this->next) { + return null; + } + + if (!$this->pragmas) { + return $this->next->validate($context); + } + + $data = []; + + foreach ($this->pragmas as $key => $handler) { + $data[$key] = $handler->enter($context); + } + + $error = $this->next->validate($context); + + foreach (array_reverse($this->pragmas, true) as $key => $handler) { + $handler->leave($context, $data[$key] ?? null); + } + + return $error; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/AbstractRefKeyword.php b/vendor/opis/json-schema/src/Keywords/AbstractRefKeyword.php new file mode 100644 index 000000000..4c8e7be19 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/AbstractRefKeyword.php @@ -0,0 +1,129 @@ +mapper = $mapper; + $this->globals = $globals; + $this->slots = $slots; + $this->keyword = $keyword; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + if ($error = $this->doValidate($context, $schema)) { + $uri = $this->lastRefUri; + $this->lastRefUri = null; + + return $this->error($schema, $context, $this->keyword, 'The data must match {keyword}', [ + 'keyword' => $this->keyword, + 'uri' => (string) $uri, + ], $error); + } + + $this->lastRefUri = null; + + return null; + } + + + abstract protected function doValidate(ValidationContext $context, Schema $schema): ?ValidationError; + + protected function setLastRefUri(?Uri $uri): void + { + $this->lastRefUri = $uri; + } + + protected function setLastRefSchema(Schema $schema): void + { + $info = $schema->info(); + + if ($info->id()) { + $this->lastRefUri = $info->id(); + } else { + $this->lastRefUri = Uri::merge(JsonPointer::pathToFragment($info->path()), $info->idBaseRoot()); + } + } + + /** + * @param ValidationContext $context + * @param Schema $schema + * @return ValidationContext + */ + protected function createContext(ValidationContext $context, Schema $schema): ValidationContext + { + return $context->create($schema, $this->mapper, $this->globals, $this->slots); + } + + /** + * @param SchemaLoader $repo + * @param JsonPointer $pointer + * @param Uri $base + * @param array|null $path + * @return null|Schema + */ + protected function resolvePointer(SchemaLoader $repo, JsonPointer $pointer, + Uri $base, ?array $path = null): ?Schema + { + if ($pointer->isAbsolute()) { + $path = (string)$pointer; + } else { + if ($pointer->hasFragment()) { + return null; + } + + $path = $path ? $pointer->absolutePath($path) : $pointer->path(); + if ($path === null) { + return null; + } + + $path = JsonPointer::pathToString($path); + } + + return $repo->loadSchemaById(Uri::merge('#' . $path, $base)); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/AdditionalItemsKeyword.php b/vendor/opis/json-schema/src/Keywords/AdditionalItemsKeyword.php new file mode 100644 index 000000000..ae0910ae2 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/AdditionalItemsKeyword.php @@ -0,0 +1,97 @@ +value = $value; + $this->index = $startIndex; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + if ($this->value === true) { + $context->markAllAsEvaluatedItems(); + return null; + } + + $data = $context->currentData(); + $count = count($data); + + if ($this->index >= $count) { + return null; + } + + if ($this->value === false) { + return $this->error($schema, $context, 'additionalItems', 'Array should not have additional items', [ + 'index' => $this->index, + ]); + } + + if (is_object($this->value) && !($this->value instanceof Schema)) { + $this->value = $context->loader()->loadObjectSchema($this->value); + } + + $object = $this->createArrayObject($context); + + $error = $this->validateIterableData($schema, $this->value, $context, $this->indexes($this->index, $count), + 'additionalItems', 'All additional array items must match schema', [], $object); + + if ($object && $object->count()) { + $context->addEvaluatedItems($object->getArrayCopy()); + } + + return $error; + } + + /** + * @param int $start + * @param int $max + * @return iterable|int[] + */ + protected function indexes(int $start, int $max): iterable + { + for ($i = $start; $i < $max; $i++) { + yield $i; + } + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/AdditionalPropertiesKeyword.php b/vendor/opis/json-schema/src/Keywords/AdditionalPropertiesKeyword.php new file mode 100644 index 000000000..b3ac59f04 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/AdditionalPropertiesKeyword.php @@ -0,0 +1,83 @@ +value = $value; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + if ($this->value === true) { + $context->markAllAsEvaluatedProperties(); + return null; + } + + $props = $context->getUncheckedProperties(); + + if (!$props) { + return null; + } + + if ($this->value === false) { + return $this->error($schema, $context, + 'additionalProperties', 'Additional object properties are not allowed: {properties}', [ + 'properties' => $props + ]); + } + + if (is_object($this->value) && !($this->value instanceof Schema)) { + $this->value = $context->loader()->loadObjectSchema($this->value); + } + + $object = $this->createArrayObject($context); + + $error = $this->validateIterableData($schema, $this->value, $context, $props, + 'additionalProperties', 'All additional object properties must match schema: {properties}', [ + 'properties' => $props + ], $object); + + if ($object && $object->count()) { + $context->addEvaluatedProperties($object->getArrayCopy()); + } + + return $error; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/AllOfKeyword.php b/vendor/opis/json-schema/src/Keywords/AllOfKeyword.php new file mode 100644 index 000000000..cbc9c619b --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/AllOfKeyword.php @@ -0,0 +1,78 @@ +value = $value; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $object = $this->createArrayObject($context); + + foreach ($this->value as $index => $value) { + if ($value === true) { + continue; + } + + if ($value === false) { + $this->addEvaluatedFromArrayObject($object, $context); + return $this->error($schema, $context, 'allOf', 'The data should match all schemas', [ + 'index' => $index, + ]); + } + + if (is_object($value) && !($value instanceof Schema)) { + $value = $this->value[$index] = $context->loader()->loadObjectSchema($value); + } + + if ($error = $context->validateSchemaWithoutEvaluated($value, null, false, $object)) { + $this->addEvaluatedFromArrayObject($object, $context); + return $this->error($schema, $context, 'allOf', 'The data should match all schemas', [ + 'index' => $index, + ], $error); + } + } + + $this->addEvaluatedFromArrayObject($object, $context); + + return null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/AnyOfKeyword.php b/vendor/opis/json-schema/src/Keywords/AnyOfKeyword.php new file mode 100644 index 000000000..1a7f3255a --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/AnyOfKeyword.php @@ -0,0 +1,94 @@ +value = $value; + $this->alwaysValid = $alwaysValid; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $object = $this->createArrayObject($context); + if ($this->alwaysValid && !$object) { + return null; + } + + $errors = []; + $ok = false; + + foreach ($this->value as $index => $value) { + if ($value === true) { + $ok = true; + if ($object) { + continue; + } + return null; + } + + if ($value === false) { + continue; + } + + if (is_object($value) && !($value instanceof Schema)) { + $value = $this->value[$index] = $context->loader()->loadObjectSchema($value); + } + + if ($error = $context->validateSchemaWithoutEvaluated($value, null, false, $object)) { + $errors[] = $error; + continue; + } + + if (!$object) { + return null; + } + $ok = true; + } + + $this->addEvaluatedFromArrayObject($object, $context); + + if ($ok) { + return null; + } + + return $this->error($schema, $context, 'anyOf', 'The data should match at least one schema', [], $errors); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/ConstDataKeyword.php b/vendor/opis/json-schema/src/Keywords/ConstDataKeyword.php new file mode 100644 index 000000000..4f5af4e9b --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/ConstDataKeyword.php @@ -0,0 +1,55 @@ +value = $value; + parent::__construct(null); + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $value = $this->value->data($context->rootData(), $context->currentDataPath(), $this); + if ($value === $this) { + return $this->error($schema, $context, 'const', 'Invalid $data', [ + 'pointer' => (string)$this->value, + ]); + } + + $this->const = $value; + $ret = parent::validate($context, $schema); + $this->const = null; + + return $ret; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/ConstKeyword.php b/vendor/opis/json-schema/src/Keywords/ConstKeyword.php new file mode 100644 index 000000000..3dde51e62 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/ConstKeyword.php @@ -0,0 +1,51 @@ +const = $const; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + if (Helper::equals($this->const, $context->currentData())) { + return null; + } + + return $this->error($schema, $context, 'const', 'The data must match the const value', [ + 'const' => $this->const + ]); + } +} diff --git a/vendor/opis/json-schema/src/Keywords/ContainsKeyword.php b/vendor/opis/json-schema/src/Keywords/ContainsKeyword.php new file mode 100644 index 000000000..8d0ee9535 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/ContainsKeyword.php @@ -0,0 +1,143 @@ +value = $value; + $this->min = $min; + $this->max = $max; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $data = $context->currentData(); + $count = count($data); + + $context->markAllAsEvaluatedItems(); + + if ($this->min > $count) { + return $this->error($schema, $context, 'minContains', 'Array must have at least {min} items', [ + 'min' => $this->min, + 'count' => $count, + ]); + } + + $isMaxNull = $this->max === null; + + if ($this->value === true) { + if ($count) { + if (!$isMaxNull && $count > $this->max) { + return $this->error($schema, $context, 'maxContains', 'Array must have at most {max} items', [ + 'max' => $this->max, + 'count' => $count, + ]); + } + return null; + } + + return $this->error($schema, $context, 'contains', 'Array must not be empty'); + } + + if ($this->value === false) { + return $this->error($schema, $context, 'contains', 'Any array is invalid'); + } + + if (is_object($this->value) && !($this->value instanceof Schema)) { + $this->value = $context->loader()->loadObjectSchema($this->value); + } + + $errors = []; + $valid = 0; + + $isMinNull = $this->min === null; + + if ($isMaxNull && $isMinNull) { + foreach ($data as $key => $item) { + $context->pushDataPath($key); + $error = $this->value->validate($context); + $context->popDataPath(); + if ($error) { + $errors[] = $error; + } else { + return null; + } + } + + return $this->error($schema, $context, 'contains', 'At least one array item must match schema', [], + $errors); + } + + foreach ($data as $key => $item) { + $context->pushDataPath($key); + $error = $this->value->validate($context); + $context->popDataPath(); + + if ($error) { + $errors[] = $error; + } else { + $valid++; + } + } + + if (!$isMinNull && $valid < $this->min) { + return $this->error($schema, $context, 'minContains', 'At least {min} array items must match schema', [ + 'min' => $this->min, + 'count' => $valid, + ]); + } + + if (!$isMaxNull && $valid > $this->max) { + return $this->error($schema, $context, 'maxContains', 'At most {max} array items must match schema', [ + 'max' => $this->max, + 'count' => $valid, + ]); + } + + if ($valid) { + return null; + } + + return $this->error($schema, $context, 'contains', 'At least one array item must match schema', [], + $errors); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/ContentEncodingKeyword.php b/vendor/opis/json-schema/src/Keywords/ContentEncodingKeyword.php new file mode 100644 index 000000000..54ff41ae4 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/ContentEncodingKeyword.php @@ -0,0 +1,77 @@ +name = $name; + $this->resolver = $resolver; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + if (!$this->resolver) { + return null; + } + + if ($this->encoding === false) { + $this->encoding = $this->resolver->resolve($this->name); + } + + if ($this->encoding === null) { + throw new UnresolvedContentEncodingException($this->name, $schema, $context); + } + + $result = $this->encoding instanceof ContentEncoding + ? $this->encoding->decode($context->currentData(), $this->name) + : ($this->encoding)($context->currentData(), $this->name); + + if ($result === null) { + return $this->error($schema, $context, 'contentEncoding', "The value must be encoded as '{encoding}'", [ + 'encoding' => $this->name, + ]); + } + + $context->setDecodedContent($result); + + return null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/ContentMediaTypeKeyword.php b/vendor/opis/json-schema/src/Keywords/ContentMediaTypeKeyword.php new file mode 100644 index 000000000..358ebdde8 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/ContentMediaTypeKeyword.php @@ -0,0 +1,83 @@ +name = $name; + $this->resolver = $resolver; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + if (!$this->resolver) { + return null; + } + + if ($this->media === false) { + $this->media = $this->resolver->resolve($this->name); + } + + if ($this->media === null) { + throw new UnresolvedContentMediaTypeException($this->name, $schema, $context); + } + + $data = $context->getDecodedContent(); + + $ok = $this->media instanceof ContentMediaType + ? $this->media->validate($data, $this->name) + : ($this->media)($data, $this->name); + if ($ok) { + return null; + } + + unset($data); + + return $this->error($schema, $context, 'contentMediaType', "The media type of the data must be '{media}'", [ + 'media' => $this->name, + ]); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/ContentSchemaKeyword.php b/vendor/opis/json-schema/src/Keywords/ContentSchemaKeyword.php new file mode 100644 index 000000000..01b3cc754 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/ContentSchemaKeyword.php @@ -0,0 +1,64 @@ +value = $value; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $data = json_decode($context->getDecodedContent(), false); + + if ($error = json_last_error() !== JSON_ERROR_NONE) { + $message = json_last_error_msg(); + + return $this->error($schema, $context, 'contentSchema', "Invalid JSON content: {message}", [ + 'error' => $error, + 'message' => $message, + ]); + } + + if (is_object($this->value) && !($this->value instanceof Schema)) { + $this->value = $context->loader()->loadObjectSchema($this->value); + } + + if ($error = $this->value->validate($context->newInstance($data, $schema))) { + return $this->error($schema, $context, 'contentSchema', "The JSON content must match schema", [], $error); + } + + return null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/DefaultKeyword.php b/vendor/opis/json-schema/src/Keywords/DefaultKeyword.php new file mode 100644 index 000000000..afaa72761 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/DefaultKeyword.php @@ -0,0 +1,53 @@ +defaults = $defaults; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $data = $context->currentData(); + + if (is_object($data)) { + foreach ($this->defaults as $name => $value) { + if (!property_exists($data, $name)) { + $data->{$name} = Helper::cloneValue($value); + } + } + } + + return null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/DependenciesKeyword.php b/vendor/opis/json-schema/src/Keywords/DependenciesKeyword.php new file mode 100644 index 000000000..15216aaf0 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/DependenciesKeyword.php @@ -0,0 +1,95 @@ +value = $value; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $data = $context->currentData(); + $object = $this->createArrayObject($context); + + foreach ($this->value as $name => $value) { + if ($value === true || !property_exists($data, $name)) { + continue; + } + + if ($value === false) { + $this->addEvaluatedFromArrayObject($object, $context); + return $this->error($schema, $context, 'dependencies', "Property '{property}' is not allowed", [ + 'property' => $name, + ]); + } + + if (is_array($value)) { + foreach ($value as $prop) { + if (!property_exists($data, $prop)) { + $this->addEvaluatedFromArrayObject($object, $context); + return $this->error($schema, $context, 'dependencies', + "Property '{missing}' property is required by property '{property}'", [ + 'property' => $name, + 'missing' => $prop, + ]); + } + } + + continue; + } + + if (is_object($value) && !($value instanceof Schema)) { + $value = $this->value[$name] = $context->loader()->loadObjectSchema($value); + } + + if ($error = $context->validateSchemaWithoutEvaluated($value, null, false, $object)) { + $this->addEvaluatedFromArrayObject($object, $context); + return $this->error($schema, $context, 'dependencies', + "The object must match dependency schema defined on property '{property}'", [ + 'property' => $name, + ], $error); + } + } + + $this->addEvaluatedFromArrayObject($object, $context); + + return null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/DependentRequiredKeyword.php b/vendor/opis/json-schema/src/Keywords/DependentRequiredKeyword.php new file mode 100644 index 000000000..90b618864 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/DependentRequiredKeyword.php @@ -0,0 +1,66 @@ +value = $value; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $data = $context->currentData(); + + foreach ($this->value as $name => $value) { + if (!property_exists($data, $name)) { + continue; + } + foreach ($value as $prop) { + if (!property_exists($data, $prop)) { + return $this->error($schema, $context, 'dependentRequired', + "'{$prop}' property is required by '{$name}' property", [ + 'property' => $name, + 'missing' => $prop, + ]); + } + } + } + + return null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/DependentSchemasKeyword.php b/vendor/opis/json-schema/src/Keywords/DependentSchemasKeyword.php new file mode 100644 index 000000000..1714bb1fa --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/DependentSchemasKeyword.php @@ -0,0 +1,76 @@ +value = (array)$value; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $data = $context->currentData(); + $object = $this->createArrayObject($context); + + foreach ($this->value as $name => $value) { + if ($value === true || !property_exists($data, $name)) { + continue; + } + + if ($value === false) { + $this->addEvaluatedFromArrayObject($object, $context); + return $this->error($schema, $context, 'dependentSchemas', "'{$name}' property is not allowed", [ + 'property' => $name, + ]); + } + + if (is_object($value) && !($value instanceof Schema)) { + $value = $this->value[$name] = $context->loader()->loadObjectSchema($value); + } + + if ($error = $context->validateSchemaWithoutEvaluated($value, null, false, $object)) { + $this->addEvaluatedFromArrayObject($object, $context); + return $this->error($schema, $context, 'dependentSchemas', + "The object must match dependency schema defined on property '{$name}'", [ + 'property' => $name, + ], $error); + } + } + + $this->addEvaluatedFromArrayObject($object, $context); + + return null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/EnumDataKeyword.php b/vendor/opis/json-schema/src/Keywords/EnumDataKeyword.php new file mode 100644 index 000000000..6e2707e07 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/EnumDataKeyword.php @@ -0,0 +1,55 @@ +value = $value; + parent::__construct([]); + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $value = $this->value->data($context->rootData(), $context->currentDataPath(), $this); + if ($value === $this || !is_array($value) || empty($value)) { + return $this->error($schema, $context, 'enum', 'Invalid $data', [ + 'pointer' => (string)$this->value, + ]); + } + + $this->enum = $this->listByType($value); + $ret = parent::validate($context, $schema); + $this->enum = null; + + return $ret; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/EnumKeyword.php b/vendor/opis/json-schema/src/Keywords/EnumKeyword.php new file mode 100644 index 000000000..92b0a0d43 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/EnumKeyword.php @@ -0,0 +1,79 @@ +enum = $this->listByType($enum); + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $type = $context->currentDataType(); + $data = $context->currentData(); + + if (isset($this->enum[$type])) { + foreach ($this->enum[$type] as $value) { + if (Helper::equals($value, $data)) { + return null; + } + } + } + + return $this->error($schema, $context, 'enum', 'The data should match one item from enum'); + } + + /** + * @param array $values + * @return array + */ + protected function listByType(array $values): array + { + $list = []; + + foreach ($values as $value) { + $type = Helper::getJsonType($value); + if (!isset($list[$type])) { + $list[$type] = []; + } + $list[$type][] = $value; + } + + return $list; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/ErrorTrait.php b/vendor/opis/json-schema/src/Keywords/ErrorTrait.php new file mode 100644 index 000000000..8d78360ec --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/ErrorTrait.php @@ -0,0 +1,55 @@ +all(); + } + } + + return new ValidationError($keyword, $schema, DataInfo::fromContext($context), $message, $args, + is_array($errors) ? $errors : []); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/ExclusiveMaximumDataKeyword.php b/vendor/opis/json-schema/src/Keywords/ExclusiveMaximumDataKeyword.php new file mode 100644 index 000000000..056c6f8c5 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/ExclusiveMaximumDataKeyword.php @@ -0,0 +1,55 @@ +value = $value; + parent::__construct(0); + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + /** @var float|int $number */ + $number = $this->value->data($context->rootData(), $context->currentDataPath(), $this); + + if ($number === $this || !(is_float($number) || is_int($number))) { + return $this->error($schema, $context, 'exclusiveMaximum', 'Invalid $data', [ + 'pointer' => (string)$this->value, + ]); + } + + $this->number = $number; + + return parent::validate($context, $schema); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/ExclusiveMaximumKeyword.php b/vendor/opis/json-schema/src/Keywords/ExclusiveMaximumKeyword.php new file mode 100644 index 000000000..66ecd61f3 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/ExclusiveMaximumKeyword.php @@ -0,0 +1,50 @@ +number = $number; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + if ($context->currentData() < $this->number) { + return null; + } + + return $this->error($schema, $context, 'exclusiveMaximum', "Number must be lower than {max}", [ + 'max' => $this->number, + ]); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/ExclusiveMinimumDataKeyword.php b/vendor/opis/json-schema/src/Keywords/ExclusiveMinimumDataKeyword.php new file mode 100644 index 000000000..92635ae4b --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/ExclusiveMinimumDataKeyword.php @@ -0,0 +1,55 @@ +value = $value; + parent::__construct(0); + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + /** @var float|int $number */ + $number = $this->value->data($context->rootData(), $context->currentDataPath(), $this); + + if ($number === $this || !(is_float($number) || is_int($number))) { + return $this->error($schema, $context, 'exclusiveMinimum', 'Invalid $data', [ + 'pointer' => (string)$this->value, + ]); + } + + $this->number = $number; + + return parent::validate($context, $schema); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/ExclusiveMinimumKeyword.php b/vendor/opis/json-schema/src/Keywords/ExclusiveMinimumKeyword.php new file mode 100644 index 000000000..90a85f5c3 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/ExclusiveMinimumKeyword.php @@ -0,0 +1,50 @@ +number = $number; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + if ($context->currentData() > $this->number) { + return null; + } + + return $this->error($schema, $context, 'exclusiveMinimum', "Number must be greater than {min}", [ + 'min' => $this->number, + ]); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/FiltersKeyword.php b/vendor/opis/json-schema/src/Keywords/FiltersKeyword.php new file mode 100644 index 000000000..e85b9617a --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/FiltersKeyword.php @@ -0,0 +1,93 @@ +filters = $filters; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $type = $context->currentDataType(); + + foreach ($this->filters as $filter) { + if (!isset($filter->types[$type])) { + throw new UnresolvedFilterException($filter->name, $type, $schema, $context); + } + + $func = $filter->types[$type]; + + if ($filter->args) { + $args = (array)$filter->args->resolve($context->rootData(), $context->currentDataPath()); + $args += $context->globals(); + } else { + $args = $context->globals(); + } + + try { + if ($func instanceof Filter) { + $ok = $func->validate($context, $schema, $args); + } else { + $ok = $func($context->currentData(), $args); + } + } catch (CustomError $error) { + return $this->error($schema, $context, '$filters', $error->getMessage(), $error->getArgs() + [ + 'filter' => $filter->name, + 'type' => $type, + 'args' => $args, + ]); + } + + if ($ok) { + unset($func, $args, $ok); + continue; + } + + return $this->error($schema, $context, '$filters', "Filter '{filter}' ({type}) was not passed", [ + 'filter' => $filter->name, + 'type' => $type, + 'args' => $args, + ]); + } + + return null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/FormatDataKeyword.php b/vendor/opis/json-schema/src/Keywords/FormatDataKeyword.php new file mode 100644 index 000000000..105baa22b --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/FormatDataKeyword.php @@ -0,0 +1,76 @@ +value = $value; + $this->resolver = $resolver; + parent::__construct('', []); + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $value = $this->value->data($context->rootData(), $context->currentDataPath(), $this); + if ($value === $this || !is_string($value)) { + return $this->error($schema, $context, 'format', 'Invalid $data', [ + 'pointer' => (string)$this->value, + ]); + } + + /** @var string $value */ + + $type = $context->currentDataType(); + + $types = [ + $type => $this->resolver->resolve($value, $type), + ]; + + if (!$types[$type] && ($super = Helper::getJsonSuperType($type))) { + $types[$super] = $this->resolver->resolve($value, $super); + unset($super); + } + + unset($type); + + $this->name = $value; + $this->types = $types; + $ret = parent::validate($context, $schema); + $this->name = $this->types = null; + + return $ret; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/FormatKeyword.php b/vendor/opis/json-schema/src/Keywords/FormatKeyword.php new file mode 100644 index 000000000..3693809f1 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/FormatKeyword.php @@ -0,0 +1,82 @@ +name = $name; + $this->types = $types; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $type = $context->currentDataType(); + + if (!isset($this->types[$type])) { + return null; + } + + $format = $this->types[$type]; + + try { + if ($format instanceof Format) { + $ok = $format->validate($context->currentData()); + } else { + $ok = $format($context->currentData()); + } + } catch (CustomError $error) { + return $this->error($schema, $context, 'format', $error->getMessage(), $error->getArgs() + [ + 'format' => $this->name, + 'type' => $type, + ]); + } + + if ($ok) { + return null; + } + + return $this->error($schema, $context, 'format', "The data must match the '{format}' format", [ + 'format' => $this->name, + 'type' => $type, + ]); + } +} diff --git a/vendor/opis/json-schema/src/Keywords/IfThenElseKeyword.php b/vendor/opis/json-schema/src/Keywords/IfThenElseKeyword.php new file mode 100644 index 000000000..730ac685e --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/IfThenElseKeyword.php @@ -0,0 +1,104 @@ +if = $if; + $this->then = $then; + $this->else = $else; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + if ($this->if === true) { + return $this->validateBranch('then', $context, $schema); + } elseif ($this->if === false) { + return $this->validateBranch('else', $context, $schema); + } + + if (is_object($this->if) && !($this->if instanceof Schema)) { + $this->if = $context->loader()->loadObjectSchema($this->if); + } + + if ($context->validateSchemaWithoutEvaluated($this->if, null, true)) { + return $this->validateBranch('else', $context, $schema); + } + + return $this->validateBranch('then', $context, $schema); + } + + /** + * @param string $branch + * @param ValidationContext $context + * @param Schema $schema + * @return ValidationError|null + */ + protected function validateBranch(string $branch, ValidationContext $context, Schema $schema): ?ValidationError + { + $value = $this->{$branch}; + + if ($value === true) { + return null; + } elseif ($value === false) { + return $this->error($schema, $context, $branch, "The data is never valid on '{branch}' branch", [ + 'branch' => $branch, + ]); + } + + if (is_object($value) && !($value instanceof Schema)) { + $value = $this->{$branch} = $context->loader()->loadObjectSchema($value); + } + + if ($error = $value->validate($context)) { + return $this->error($schema, $context, $branch, "The data is not valid on '{branch}' branch", [ + 'branch' => $branch, + ], $error); + } + + return null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/ItemsKeyword.php b/vendor/opis/json-schema/src/Keywords/ItemsKeyword.php new file mode 100644 index 000000000..47264552c --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/ItemsKeyword.php @@ -0,0 +1,162 @@ +value = $value; + $this->alwaysValid = $alwaysValid; + + if (is_array($value)) { + $this->count = count($value); + } + + $this->keyword = $keyword; + $this->startIndex = $startIndex; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + if ($this->alwaysValid || $this->value === true) { + if ($this->count === -1) { + $context->markAllAsEvaluatedItems(); + } else { + $context->markCountAsEvaluatedItems($this->count); + } + return null; + } + + $count = count($context->currentData()); + + if ($this->startIndex >= $count) { + // Already validated by other keyword + return null; + } + + if ($this->value === false) { + if ($count === 0) { + return null; + } + + return $this->error($schema, $context, $this->keyword, 'Array must be empty'); + } + + if ($this->count >= 0) { + + $errors = $this->errorContainer($context->maxErrors()); + $max = min($count, $this->count); + $evaluated = []; + + for ($i = $this->startIndex; $i < $max; $i++) { + if ($this->value[$i] === true) { + $evaluated[] = $i; + continue; + } + + if ($this->value[$i] === false) { + $context->addEvaluatedItems($evaluated); + return $this->error($schema, $context, $this->keyword, "Array item at index {index} is not allowed", [ + 'index' => $i, + ]); + } + + if (is_object($this->value[$i]) && !($this->value[$i] instanceof Schema)) { + $this->value[$i] = $context->loader()->loadObjectSchema($this->value[$i]); + } + + $context->pushDataPath($i); + $error = $this->value[$i]->validate($context); + $context->popDataPath(); + + if ($error) { + $errors->add($error); + if ($errors->isFull()) { + break; + } + } else { + $evaluated[] = $i; + } + } + + $context->addEvaluatedItems($evaluated); + + if ($errors->isEmpty()) { + return null; + } + + return $this->error($schema, $context, $this->keyword, 'Array items must match corresponding schemas', [], + $errors); + } + + if (is_object($this->value) && !($this->value instanceof Schema)) { + $this->value = $context->loader()->loadObjectSchema($this->value); + } + + $object = $this->createArrayObject($context); + + $error = $this->validateIterableData($schema, $this->value, $context, $this->indexes($this->startIndex, $count), + $this->keyword, 'All array items must match schema', [], $object); + + if ($object && $object->count()) { + $context->addEvaluatedItems($object->getArrayCopy()); + } + + return $error; + } + + /** + * @param int $start + * @param int $max + * @return iterable|int[] + */ + protected function indexes(int $start, int $max): iterable + { + for ($i = $start; $i < $max; $i++) { + yield $i; + } + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/IterableDataValidationTrait.php b/vendor/opis/json-schema/src/Keywords/IterableDataValidationTrait.php new file mode 100644 index 000000000..99754ec86 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/IterableDataValidationTrait.php @@ -0,0 +1,110 @@ +errorContainer($context->maxErrors()); + + if ($keys) { + foreach ($iterator as $key) { + $context->pushDataPath($key); + $error = $schema->validate($context); + $context->popDataPath(); + + if ($error) { + if (!$container->isFull()) { + $container->add($error); + } + } else { + $keys[] = $key; + } + } + } else { + foreach ($iterator as $key) { + $context->pushDataPath($key); + $error = $schema->validate($context); + $context->popDataPath(); + + if ($error && $container->add($error)->isFull()) { + break; + } + } + } + + return $container; + } + + /** + * @param Schema $parentSchema + * @param Schema $schema + * @param ValidationContext $context + * @param iterable $iterator + * @param string $keyword + * @param string $message + * @param array $args + * @param ArrayObject|null $visited_keys + * @return ValidationError|null + */ + protected function validateIterableData( + Schema $parentSchema, + Schema $schema, + ValidationContext $context, + iterable $iterator, + string $keyword, + string $message, + array $args = [], + ?ArrayObject $visited_keys = null + ): ?ValidationError { + $errors = $this->iterateAndValidate($schema, $context, $iterator, $visited_keys); + + if ($errors->isEmpty()) { + return null; + } + + return $this->error($parentSchema, $context, $keyword, $message, $args, $errors); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/MaxItemsDataKeyword.php b/vendor/opis/json-schema/src/Keywords/MaxItemsDataKeyword.php new file mode 100644 index 000000000..7e5379738 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/MaxItemsDataKeyword.php @@ -0,0 +1,55 @@ +value = $value; + parent::__construct(0); + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + /** @var int $count */ + $count = $this->value->data($context->rootData(), $context->currentDataPath(), $this); + + if ($count === $this || !is_int($count) || $count < 0) { + return $this->error($schema, $context, 'maxItems', 'Invalid $data', [ + 'pointer' => (string)$this->value, + ]); + } + + $this->count = $count; + + return parent::validate($context, $schema); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/MaxItemsKeyword.php b/vendor/opis/json-schema/src/Keywords/MaxItemsKeyword.php new file mode 100644 index 000000000..4003d2fd5 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/MaxItemsKeyword.php @@ -0,0 +1,58 @@ +count = $count; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $count = count($context->currentData()); + + if ($count <= $this->count) { + return null; + } + + return $this->error($schema, $context, "maxItems", + "Array should have at most {max} items, {count} found", [ + 'max' => $this->count, + 'count' => $count, + ]); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/MaxLengthDataKeyword.php b/vendor/opis/json-schema/src/Keywords/MaxLengthDataKeyword.php new file mode 100644 index 000000000..ea2366d28 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/MaxLengthDataKeyword.php @@ -0,0 +1,55 @@ +value = $value; + parent::__construct(0); + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + /** @var int $length */ + $length = $this->value->data($context->rootData(), $context->currentDataPath(), $this); + + if ($length === $this || !is_int($length) || $length < 0) { + return $this->error($schema, $context, 'maxLength', 'Invalid $data', [ + 'pointer' => (string)$this->value, + ]); + } + + $this->length = $length; + + return parent::validate($context, $schema); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/MaxLengthKeyword.php b/vendor/opis/json-schema/src/Keywords/MaxLengthKeyword.php new file mode 100644 index 000000000..81b3cc549 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/MaxLengthKeyword.php @@ -0,0 +1,58 @@ +length = $length; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $length = $context->getStringLength(); + + if ($length <= $this->length) { + return null; + } + + return $this->error($schema, $context, 'maxLength', "Maximum string length is {max}, found {length}", + [ + 'max' => $this->length, + 'length' => $length, + ]); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/MaxPropertiesDataKeyword.php b/vendor/opis/json-schema/src/Keywords/MaxPropertiesDataKeyword.php new file mode 100644 index 000000000..f48f4398d --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/MaxPropertiesDataKeyword.php @@ -0,0 +1,55 @@ +value = $value; + parent::__construct(0); + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + /** @var int $count */ + $count = $this->value->data($context->rootData(), $context->currentDataPath(), $this); + + if ($count === $this || !is_int($count) || $count < 0) { + return $this->error($schema, $context, 'maxProperties', 'Invalid $data', [ + 'pointer' => (string)$this->value, + ]); + } + + $this->count = $count; + + return parent::validate($context, $schema); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/MaxPropertiesKeywords.php b/vendor/opis/json-schema/src/Keywords/MaxPropertiesKeywords.php new file mode 100644 index 000000000..34fa0d60b --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/MaxPropertiesKeywords.php @@ -0,0 +1,58 @@ +count = $count; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $count = count($context->getObjectProperties()); + + if ($count <= $this->count) { + return null; + } + + return $this->error($schema, $context, 'maxProperties', + "Object must have at most {max} properties, {count} found", [ + 'max' => $this->count, + 'count' => $count, + ]); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/MaximumDataKeyword.php b/vendor/opis/json-schema/src/Keywords/MaximumDataKeyword.php new file mode 100644 index 000000000..55b9dfd5b --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/MaximumDataKeyword.php @@ -0,0 +1,55 @@ +value = $value; + parent::__construct(0); + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + /** @var float|int $number */ + $number = $this->value->data($context->rootData(), $context->currentDataPath(), $this); + + if ($number === $this || !(is_float($number) || is_int($number))) { + return $this->error($schema, $context, 'maximum', 'Invalid $data', [ + 'pointer' => (string)$this->value, + ]); + } + + $this->number = $number; + + return parent::validate($context, $schema); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/MaximumKeyword.php b/vendor/opis/json-schema/src/Keywords/MaximumKeyword.php new file mode 100644 index 000000000..6888a2752 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/MaximumKeyword.php @@ -0,0 +1,54 @@ +number = $number; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + if ($context->currentData() <= $this->number) { + return null; + } + + return $this->error($schema, $context, 'maximum', "Number must be lower than or equal to {max}", [ + 'max' => $this->number, + ]); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/MinItemsDataKeyword.php b/vendor/opis/json-schema/src/Keywords/MinItemsDataKeyword.php new file mode 100644 index 000000000..1ace7fdc0 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/MinItemsDataKeyword.php @@ -0,0 +1,55 @@ +value = $value; + parent::__construct(0); + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + /** @var int $count */ + $count = $this->value->data($context->rootData(), $context->currentDataPath(), $this); + + if ($count === $this || !is_int($count) || $count < 0) { + return $this->error($schema, $context, 'minItems', 'Invalid $data', [ + 'pointer' => (string)$this->value, + ]); + } + + $this->count = $count; + + return parent::validate($context, $schema); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/MinItemsKeyword.php b/vendor/opis/json-schema/src/Keywords/MinItemsKeyword.php new file mode 100644 index 000000000..bcc0d830f --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/MinItemsKeyword.php @@ -0,0 +1,58 @@ +count = $count; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $count = count($context->currentData()); + + if ($count >= $this->count) { + return null; + } + + return $this->error($schema, $context, "minItems", + "Array should have at least {min} items, {count} found", [ + 'min' => $this->count, + 'count' => $count, + ]); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/MinLengthDataKeyword.php b/vendor/opis/json-schema/src/Keywords/MinLengthDataKeyword.php new file mode 100644 index 000000000..57d78c85e --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/MinLengthDataKeyword.php @@ -0,0 +1,55 @@ +value = $value; + parent::__construct(0); + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + /** @var int $length */ + $length = $this->value->data($context->rootData(), $context->currentDataPath(), $this); + + if ($length === $this || !is_int($length) || $length < 0) { + return $this->error($schema, $context, 'minLength', 'Invalid $data', [ + 'pointer' => (string)$this->value, + ]); + } + + $this->length = $length; + + return parent::validate($context, $schema); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/MinLengthKeyword.php b/vendor/opis/json-schema/src/Keywords/MinLengthKeyword.php new file mode 100644 index 000000000..7c446d0e9 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/MinLengthKeyword.php @@ -0,0 +1,61 @@ +length = $length; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + if ($this->length === 0) { + return null; + } + + $length = $context->getStringLength(); + + if ($length >= $this->length) { + return null; + } + + return $this->error($schema, $context, 'minLength', "Minimum string length is {min}, found {length}", [ + 'min' => $this->length, + 'length' => $length, + ]); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/MinPropertiesDataKeyword.php b/vendor/opis/json-schema/src/Keywords/MinPropertiesDataKeyword.php new file mode 100644 index 000000000..e1b2a2dae --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/MinPropertiesDataKeyword.php @@ -0,0 +1,55 @@ +value = $value; + parent::__construct(0); + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + /** @var int $count */ + $count = $this->value->data($context->rootData(), $context->currentDataPath(), $this); + + if ($count === $this || !is_int($count) || $count < 0) { + return $this->error($schema, $context, 'minProperties', 'Invalid $data', [ + 'pointer' => (string)$this->value, + ]); + } + + $this->count = $count; + + return parent::validate($context, $schema); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/MinPropertiesKeyword.php b/vendor/opis/json-schema/src/Keywords/MinPropertiesKeyword.php new file mode 100644 index 000000000..7836168d8 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/MinPropertiesKeyword.php @@ -0,0 +1,58 @@ +count = $count; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $count = count($context->getObjectProperties()); + + if ($this->count <= $count) { + return null; + } + + return $this->error($schema, $context, 'minProperties', + "Object must have at least {min} properties, {count} found", [ + 'min' => $this->count, + 'count' => $count, + ]); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/MinimumDataKeyword.php b/vendor/opis/json-schema/src/Keywords/MinimumDataKeyword.php new file mode 100644 index 000000000..37140c360 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/MinimumDataKeyword.php @@ -0,0 +1,55 @@ +value = $value; + parent::__construct(0); + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + /** @var float|int $number */ + $number = $this->value->data($context->rootData(), $context->currentDataPath(), $this); + + if ($number === $this || !(is_float($number) || is_int($number))) { + return $this->error($schema, $context, 'minimum', 'Invalid $data', [ + 'pointer' => (string)$this->value, + ]); + } + + $this->number = $number; + + return parent::validate($context, $schema); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/MinimumKeyword.php b/vendor/opis/json-schema/src/Keywords/MinimumKeyword.php new file mode 100644 index 000000000..a499aefbb --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/MinimumKeyword.php @@ -0,0 +1,54 @@ +number = $number; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + if ($context->currentData() >= $this->number) { + return null; + } + + return $this->error($schema, $context, 'minimum', "Number must be greater than or equal to {min}", [ + 'min' => $this->number, + ]); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/MultipleOfDataKeyword.php b/vendor/opis/json-schema/src/Keywords/MultipleOfDataKeyword.php new file mode 100644 index 000000000..532ee8a0d --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/MultipleOfDataKeyword.php @@ -0,0 +1,55 @@ +value = $value; + parent::__construct(0); + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + /** @var float|int $number */ + $number = $this->value->data($context->rootData(), $context->currentDataPath(), $this); + + if ($number === $this || !(is_float($number) || is_int($number)) || $number <= 0) { + return $this->error($schema, $context, 'multipleOf', 'Invalid $data', [ + 'pointer' => (string)$this->value, + ]); + } + + $this->number = $number; + + return parent::validate($context, $schema); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/MultipleOfKeyword.php b/vendor/opis/json-schema/src/Keywords/MultipleOfKeyword.php new file mode 100644 index 000000000..28ac8f6a9 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/MultipleOfKeyword.php @@ -0,0 +1,55 @@ +number = $number; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + if (Helper::isMultipleOf($context->currentData(), $this->number)) { + return null; + } + + return $this->error($schema, $context, 'multipleOf', "Number must be a multiple of {divisor}", [ + 'divisor' => $this->number, + ]); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/NotKeyword.php b/vendor/opis/json-schema/src/Keywords/NotKeyword.php new file mode 100644 index 000000000..3a5acd448 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/NotKeyword.php @@ -0,0 +1,66 @@ +value = $value; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + if ($this->value === false) { + return null; + } + if ($this->value === true) { + return $this->error($schema, $context, 'not', "The data is never valid"); + } + + if (is_object($this->value) && !($this->value instanceof Schema)) { + $this->value = $context->loader()->loadObjectSchema($this->value); + } + + $error = $context->validateSchemaWithoutEvaluated($this->value, 1); + + if ($error) { + return null; + } + + return $this->error($schema, $context, 'not', 'The data must not match schema'); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/OfTrait.php b/vendor/opis/json-schema/src/Keywords/OfTrait.php new file mode 100644 index 000000000..bcb0b2c13 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/OfTrait.php @@ -0,0 +1,45 @@ +trackUnevaluated() ? new ArrayObject() : null; + } + + protected function addEvaluatedFromArrayObject(?ArrayObject $object, ValidationContext $context): void + { + if (!$object || !$object->count()) { + return; + } + + foreach ($object as $value) { + if (isset($value['properties'])) { + $context->addEvaluatedProperties($value['properties']); + } + if (isset($value['items'])) { + $context->addEvaluatedItems($value['items']); + } + } + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/OneOfKeyword.php b/vendor/opis/json-schema/src/Keywords/OneOfKeyword.php new file mode 100644 index 000000000..12473b6ec --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/OneOfKeyword.php @@ -0,0 +1,98 @@ +value = $value; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $count = 0; + $matchedIndex = -1; + $object = $this->createArrayObject($context); + $errors = []; + + foreach ($this->value as $index => $value) { + if ($value === false) { + continue; + } + + if ($value === true) { + if (++$count > 1) { + $this->addEvaluatedFromArrayObject($object, $context); + return $this->error($schema, $context, 'oneOf', 'The data should match exactly one schema', [ + 'matched' => [$matchedIndex, $index], + ]); + } + + $matchedIndex = $index; + continue; + } + + if (is_object($value) && !($value instanceof Schema)) { + $value = $this->value[$index] = $context->loader()->loadObjectSchema($value); + } + + $error = $context->validateSchemaWithoutEvaluated($value, null, false, $object); + if ($error) { + $errors[] = $error; + } else { + if (++$count > 1) { + $this->addEvaluatedFromArrayObject($object, $context); + return $this->error($schema, $context, 'oneOf', 'The data should match exactly one schema', [ + 'matched' => [$matchedIndex, $index], + ]); + } + $matchedIndex = $index; + } + } + + $this->addEvaluatedFromArrayObject($object, $context); + + if ($count === 1) { + return null; + } + + return $this->error($schema, $context, 'oneOf', 'The data should match exactly one schema', [ + 'matched' => [], + ], $errors); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/PatternDataKeyword.php b/vendor/opis/json-schema/src/Keywords/PatternDataKeyword.php new file mode 100644 index 000000000..1fa7c15d1 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/PatternDataKeyword.php @@ -0,0 +1,56 @@ +value = $value; + parent::__construct(''); + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $pattern = $this->value->data($context->rootData(), $context->currentDataPath(), $this); + if ($pattern === $this || !is_string($pattern) || !Helper::isValidPattern($pattern)) { + return $this->error($schema, $context, 'pattern', 'Invalid $data', [ + 'pointer' => (string)$this->value, + ]); + } + + $this->pattern = $pattern; + $this->regex = Helper::patternToRegex($pattern); + $ret = parent::validate($context, $schema); + $this->pattern = $this->regex = null; + + return $ret; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/PatternKeyword.php b/vendor/opis/json-schema/src/Keywords/PatternKeyword.php new file mode 100644 index 000000000..b16cbc186 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/PatternKeyword.php @@ -0,0 +1,53 @@ +pattern = $pattern; + $this->regex = Helper::patternToRegex($pattern); + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + if (preg_match($this->regex, $context->currentData())) { + return null; + } + + return $this->error($schema, $context, 'pattern', "The string should match pattern: {pattern}", [ + 'pattern' => $this->pattern, + ]); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/PatternPropertiesKeyword.php b/vendor/opis/json-schema/src/Keywords/PatternPropertiesKeyword.php new file mode 100644 index 000000000..7980a8517 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/PatternPropertiesKeyword.php @@ -0,0 +1,119 @@ +value = $value; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $props = $context->getObjectProperties(); + + if (!$props) { + return null; + } + + $checked = []; + + foreach ($this->value as $pattern => $value) { + if ($value === true) { + iterator_to_array($this->matchedProperties($pattern, $props, $checked)); + continue; + } + + if ($value === false) { + $list = iterator_to_array($this->matchedProperties($pattern, $props, $checked)); + + if ($list) { + if ($context->trackUnevaluated()) { + $context->addEvaluatedProperties(array_diff(array_keys($checked), $list)); + } + return $this->error($schema, $context, 'patternProperties', "Object properties that match pattern '{pattern}' are not allowed", [ + 'pattern' => $pattern, + 'forbidden' => $list, + ]); + } + + unset($list); + continue; + } + + if (is_object($value) && !($value instanceof Schema)) { + $value = $this->value[$pattern] = $context->loader()->loadObjectSchema($value); + } + + $subErrors = $this->iterateAndValidate($value, $context, $this->matchedProperties($pattern, $props, $checked)); + + if (!$subErrors->isEmpty()) { + if ($context->trackUnevaluated()) { + $context->addEvaluatedProperties(array_keys($checked)); + } + return $this->error($schema, $context, 'patternProperties', "Object properties that match pattern '{pattern}' must also match pattern's schema", [ + 'pattern' => $pattern, + ], $subErrors); + } + + unset($subErrors); + } + + if ($checked) { + $checked = array_keys($checked); + $context->addCheckedProperties($checked); + $context->addEvaluatedProperties($checked); + } + + return null; + } + + /** + * @param string $pattern + * @param array $props + * @param array $checked + * @return Traversable|string[] + */ + protected function matchedProperties(string $pattern, array $props, array &$checked): Traversable + { + $pattern = Helper::patternToRegex($pattern); + + foreach ($props as $prop) { + if (preg_match($pattern, (string)$prop)) { + $checked[$prop] = true; + yield $prop; + } + } + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/PointerRefKeyword.php b/vendor/opis/json-schema/src/Keywords/PointerRefKeyword.php new file mode 100644 index 000000000..a11142baf --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/PointerRefKeyword.php @@ -0,0 +1,54 @@ +pointer = $pointer; + } + + protected function doValidate(ValidationContext $context, Schema $schema): ?ValidationError + { + if ($this->resolved === false) { + $info = $schema->info(); + $this->resolved = $this->resolvePointer($context->loader(), $this->pointer, $info->idBaseRoot(), $info->path()); + } + + if ($this->resolved === null) { + throw new UnresolvedReferenceException((string)$this->pointer, $schema, $context); + } + + return $this->resolved->validate($this->createContext($context, $schema)); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/PropertiesKeyword.php b/vendor/opis/json-schema/src/Keywords/PropertiesKeyword.php new file mode 100644 index 000000000..5af65571a --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/PropertiesKeyword.php @@ -0,0 +1,109 @@ +properties = $properties; + $this->propertyKeys = array_keys($properties); + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + if (!$this->properties) { + return null; + } + + $checked = []; + $evaluated = []; + + $data = $context->currentData(); + + $errors = $this->errorContainer($context->maxErrors()); + + foreach ($this->properties as $name => $prop) { + if (!property_exists($data, $name)) { + continue; + } + + $checked[] = $name; + + if ($prop === true) { + $evaluated[] = $name; + continue; + } + + if ($prop === false) { + $context->addEvaluatedProperties($evaluated); + return $this->error($schema, $context, 'properties', "Property '{property}' is not allowed", [ + 'property' => $name, + ]); + } + + if (is_object($prop) && !($prop instanceof Schema)) { + $prop = $this->properties[$name] = $context->loader()->loadObjectSchema($prop); + } + + $context->pushDataPath($name); + $error = $prop->validate($context); + $context->popDataPath(); + + if ($error) { + $errors->add($error); + if ($errors->isFull()) { + break; + } + } else { + $evaluated[] = $name; + } + } + + $context->addEvaluatedProperties($evaluated); + + if (!$errors->isEmpty()) { + return $this->error($schema, $context, 'properties', "The properties must match schema: {properties}", [ + 'properties' => array_values(array_diff($checked, $evaluated)) + ], $errors); + } + unset($errors); + + $context->addCheckedProperties($checked); + + return null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/PropertyNamesKeyword.php b/vendor/opis/json-schema/src/Keywords/PropertyNamesKeyword.php new file mode 100644 index 000000000..4012ca971 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/PropertyNamesKeyword.php @@ -0,0 +1,74 @@ +value = $value; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + if ($this->value === true) { + return null; + } + + $props = $context->getObjectProperties(); + if (!$props) { + return null; + } + + if ($this->value === false) { + return $this->error($schema, $context, 'propertyNames', "No properties are allowed"); + } + + if (is_object($this->value) && !($this->value instanceof Schema)) { + $this->value = $context->loader()->loadObjectSchema($this->value); + } + + foreach ($props as $prop) { + if ($error = $this->value->validate($context->newInstance($prop, $schema))) { + return $this->error($schema, $context, 'propertyNames', "Property '{property}' must match schema", [ + 'property' => $prop, + ], $error); + } + } + + return null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/RecursiveRefKeyword.php b/vendor/opis/json-schema/src/Keywords/RecursiveRefKeyword.php new file mode 100644 index 000000000..d46000144 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/RecursiveRefKeyword.php @@ -0,0 +1,138 @@ +uri = $uri; + $this->anchor = $anchor; + $this->anchorValue = $anchorValue; + } + + /** + * @inheritDoc + */ + public function doValidate(ValidationContext $context, Schema $schema): ?ValidationError + { + if ($this->resolved === false) { + $this->resolved = $context->loader()->loadSchemaById($this->uri); + } + + if ($this->resolved === null) { + throw new UnresolvedReferenceException((string)$this->uri, $schema, $context); + } + + $new_context = $this->createContext($context, $schema); + + if (!$this->hasRecursiveAnchor($this->resolved)) { + $this->setLastRefSchema($this->resolved); + return $this->resolved->validate($new_context); + } + + $ok_sender = $this->resolveSchema($context); + + if (!$ok_sender) { + $this->setLastRefSchema($this->resolved); + return $this->resolved->validate($new_context); + } + + $this->setLastRefSchema($ok_sender); + + return $ok_sender->validate($new_context); + } + + protected function resolveSchema(ValidationContext $context): ?Schema + { + $ok = null; + $loader = $context->loader(); + + while ($context) { + $sender = $context->sender(); + + if (!$sender) { + break; + } + + if (!$this->hasRecursiveAnchor($sender)) { + if ($sender->info()->id()) { + // id without recursiveAnchor + break; + } + + $sender = $loader->loadSchemaById($sender->info()->root()); + if (!$sender || !$this->hasRecursiveAnchor($sender)) { + // root without recursiveAnchor + break; + } + } + + if ($sender->info()->id()) { + // id with recursiveAnchor + $ok = $sender; + } else { + // root with recursiveAnchor + $ok = $loader->loadSchemaById($sender->info()->root()); + } + + $context = $context->parent(); + } + + return $ok; + } + + protected function hasRecursiveAnchor(?Schema $schema): bool + { + if (!$schema) { + return false; + } + + $info = $schema->info(); + + if (!$info->isObject()) { + return false; + } + + $data = $info->data(); + + if (!property_exists($data, $this->anchor)) { + return false; + } + + return $data->{$this->anchor} === $this->anchorValue; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/RequiredDataKeyword.php b/vendor/opis/json-schema/src/Keywords/RequiredDataKeyword.php new file mode 100644 index 000000000..d518244ef --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/RequiredDataKeyword.php @@ -0,0 +1,85 @@ +value = $value; + $this->filter = $filter; + parent::__construct([]); + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $required = $this->value->data($context->rootData(), $context->currentDataPath(), $this); + if ($required === $this || !is_array($required) || !$this->requiredPropsAreValid($required)) { + return $this->error($schema, $context, 'required', 'Invalid $data', [ + 'pointer' => (string)$this->value, + ]); + } + + $required = array_unique($required); + + if ($this->filter) { + $required = array_filter($required, $this->filter); + } + + if (!$required) { + return null; + } + + $this->required = $required; + $ret = parent::validate($context, $schema); + $this->required = null; + + return $ret; + } + + /** + * @param array $props + * @return bool + */ + protected function requiredPropsAreValid(array $props): bool + { + foreach ($props as $prop) { + if (!is_string($prop)) { + return false; + } + } + + return true; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/RequiredKeyword.php b/vendor/opis/json-schema/src/Keywords/RequiredKeyword.php new file mode 100644 index 000000000..005e2dcf9 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/RequiredKeyword.php @@ -0,0 +1,68 @@ +required = $required; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $data = $context->currentData(); + $max = $context->maxErrors(); + $list = []; + + foreach ($this->required as $name) { + if (!property_exists($data, $name)) { + $list[] = $name; + if (--$max <= 0) { + break; + } + } + } + + if (!$list) { + return null; + } + + return $this->error($schema, $context, 'required', 'The required properties ({missing}) are missing', [ + 'missing' => $list, + ]); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/SlotsKeyword.php b/vendor/opis/json-schema/src/Keywords/SlotsKeyword.php new file mode 100644 index 000000000..c843845b0 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/SlotsKeyword.php @@ -0,0 +1,151 @@ +slots = $slots; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $newContext = $context->newInstance($context->currentData(), $schema); + + foreach ($this->slots as $name => $fallback) { + $slot = $this->resolveSlotSchema($name, $context); + + if ($slot === null) { + $save = true; + if (is_string($fallback)) { + $save = false; + $fallback = $this->resolveSlot($fallback, $context); + } + + if ($fallback === true) { + continue; + } + + if ($fallback === false) { + return $this->error($schema, $context, '$slots', "Required slot '{slot}' is missing", [ + 'slot' => $name, + ]); + } + + if (is_object($fallback) && !($fallback instanceof Schema)) { + $fallback = $context->loader()->loadObjectSchema($fallback); + if ($save) { + $this->slots[$name] = $fallback; + } + } + + $slot = $fallback; + } + + if ($error = $slot->validate($newContext)) { + return $this->error($schema, $context,'$slots', "Schema for slot '{slot}' was not matched", [ + 'slot' => $name, + ], $error); + } + } + + return null; + } + + /** + * @param string $name + * @param ValidationContext $context + * @return Schema|null + */ + protected function resolveSlotSchema(string $name, ValidationContext $context): ?Schema + { + do { + $slot = $context->slot($name); + } while ($slot === null && $context = $context->parent()); + + return $slot; + } + + /** + * @param string $name + * @param ValidationContext $context + * @return bool|Schema + */ + protected function resolveSlot(string $name, ValidationContext $context) + { + $slot = $this->resolveSlotSchema($name, $context); + + if ($slot !== null) { + return $slot; + } + + if (!isset($this->slots[$name])) { + return false; + } + + $slot = $this->slots[$name]; + + if (is_bool($slot)) { + return $slot; + } + + if (is_object($slot)) { + if ($slot instanceof Schema) { + return $slot; + } + + $slot = $context->loader()->loadObjectSchema($slot); + $this->slots[$name] = $slot; + return $slot; + } + + if (!is_string($slot)) { + // Looks like the slot is missing + return false; + } + + if (in_array($slot, $this->stack)) { + // Recursive + return false; + } + + $this->stack[] = $slot; + $slot = $this->resolveSlot($slot, $context); + array_pop($this->stack); + + return $slot; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/TemplateRefKeyword.php b/vendor/opis/json-schema/src/Keywords/TemplateRefKeyword.php new file mode 100644 index 000000000..d9876e22d --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/TemplateRefKeyword.php @@ -0,0 +1,119 @@ +template = $template; + $this->vars = $vars; + $this->allowRelativeJsonPointerInRef = $allowRelativeJsonPointerInRef; + } + + protected function doValidate(ValidationContext $context, Schema $schema): ?ValidationError + { + if ($this->vars) { + $vars = $this->vars->resolve($context->rootData(), $context->currentDataPath()); + if (!is_array($vars)) { + $vars = (array)$vars; + } + $vars += $context->globals(); + } else { + $vars = $context->globals(); + } + + $ref = $this->template->resolve($vars); + + $key = isset($ref[32]) ? md5($ref) : $ref; + + if (!array_key_exists($key, $this->cached)) { + $this->cached[$key] = $this->resolveRef($ref, $context->loader(), $schema); + } + + $resolved = $this->cached[$key]; + unset($key); + + if (!$resolved) { + throw new UnresolvedReferenceException($ref, $schema, $context); + } + + return $resolved->validate($this->createContext($context, $schema)); + } + + /** + * @param string $ref + * @param SchemaLoader $repo + * @param Schema $schema + * @return null|Schema + */ + protected function resolveRef(string $ref, SchemaLoader $repo, Schema $schema): ?Schema + { + if ($ref === '') { + return null; + } + + $baseUri = $schema->info()->idBaseRoot(); + + if ($ref === '#') { + return $repo->loadSchemaById($baseUri); + } + + // Check if is pointer + if ($ref[0] === '#') { + if ($pointer = JsonPointer::parse(substr($ref, 1))) { + if ($pointer->isAbsolute()) { + return $this->resolvePointer($repo, $pointer, $baseUri); + } + unset($pointer); + } + } elseif ($this->allowRelativeJsonPointerInRef && ($pointer = JsonPointer::parse($ref))) { + if ($pointer->isRelative()) { + return $this->resolvePointer($repo, $pointer, $baseUri, $schema->info()->path()); + } + unset($pointer); + } + + $ref = Uri::merge($ref, $baseUri, true); + + if ($ref === null || !$ref->isAbsolute()) { + return null; + } + + return $repo->loadSchemaById($ref); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/TypeKeyword.php b/vendor/opis/json-schema/src/Keywords/TypeKeyword.php new file mode 100644 index 000000000..af4331b41 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/TypeKeyword.php @@ -0,0 +1,58 @@ +type = $type; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $type = $context->currentDataType(); + if ($type && Helper::jsonTypeMatches($type, $this->type)) { + return null; + } + + return $this->error($schema, $context, 'type', 'The data ({type}) must match the type: {expected}', [ + 'expected' => $this->type, + 'type' => $type, + ]); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/URIRefKeyword.php b/vendor/opis/json-schema/src/Keywords/URIRefKeyword.php new file mode 100644 index 000000000..ab8bebae6 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/URIRefKeyword.php @@ -0,0 +1,55 @@ +uri = $uri; + } + + protected function doValidate(ValidationContext $context, Schema $schema): ?ValidationError + { + if ($this->resolved === false) { + $this->resolved = $context->loader()->loadSchemaById($this->uri); + } + + if ($this->resolved === null) { + throw new UnresolvedReferenceException((string)$this->uri, $schema, $context); + } + + $this->setLastRefSchema($this->resolved); + + return $this->resolved->validate($this->createContext($context, $schema)); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/UnevaluatedItemsKeyword.php b/vendor/opis/json-schema/src/Keywords/UnevaluatedItemsKeyword.php new file mode 100644 index 000000000..a9673fea9 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/UnevaluatedItemsKeyword.php @@ -0,0 +1,75 @@ +value = $value; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $unevaluated = $context->getUnevaluatedItems(); + + if (!$unevaluated) { + return null; + } + + if ($this->value === true) { + $context->addEvaluatedItems($unevaluated); + return null; + } + + if ($this->value === false) { + return $this->error($schema, $context, 'unevaluatedItems', + 'Unevaluated array items are not allowed: {indexes}', [ + 'indexes' => $unevaluated, + ]); + } + + if (is_object($this->value) && !($this->value instanceof Schema)) { + $this->value = $context->loader()->loadObjectSchema($this->value); + } + + $object = $this->createArrayObject($context); + + $error = $this->validateIterableData($schema, $this->value, $context, $unevaluated, + 'unevaluatedItems', 'All unevaluated array items must match schema: {indexes}', [ + 'indexes' => $unevaluated, + ], $object); + + if ($object && $object->count()) { + $context->addEvaluatedItems($object->getArrayCopy()); + } + + return $error; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/UnevaluatedPropertiesKeyword.php b/vendor/opis/json-schema/src/Keywords/UnevaluatedPropertiesKeyword.php new file mode 100644 index 000000000..ac1a48549 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/UnevaluatedPropertiesKeyword.php @@ -0,0 +1,77 @@ +value = $value; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $unevaluated = $context->getUnevaluatedProperties(); + + if (!$unevaluated) { + return null; + } + + if ($this->value === true) { + $context->addEvaluatedProperties($unevaluated); + return null; + } + + if ($this->value === false) { + return $this->error($schema, $context, 'unevaluatedProperties', + 'Unevaluated object properties not allowed: {properties}', [ + 'properties' => $unevaluated, + ]); + } + + if (is_object($this->value) && !($this->value instanceof Schema)) { + $this->value = $context->loader()->loadObjectSchema($this->value); + } + + $object = $this->createArrayObject($context); + + $error = $this->validateIterableData($schema, $this->value, $context, $unevaluated, + 'unevaluatedProperties', 'All unevaluated object properties must match schema: {properties}', [ + 'properties' => $unevaluated, + ], $object); + + + if ($object && $object->count()) { + $context->addEvaluatedProperties($object->getArrayCopy()); + } + + return $error; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/UniqueItemsDataKeyword.php b/vendor/opis/json-schema/src/Keywords/UniqueItemsDataKeyword.php new file mode 100644 index 000000000..cd860e8d2 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/UniqueItemsDataKeyword.php @@ -0,0 +1,51 @@ +value = $value; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context, Schema $schema): ?ValidationError + { + $value = $this->value->data($context->rootData(), $context->currentDataPath(), $this); + + if ($value === $this || !is_bool($value)) { + return $this->error($schema, $context, 'uniqueItems', 'Invalid $data', [ + 'pointer' => (string)$this->value, + ]); + } + + return $value ? parent::validate($context, $schema) : null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Keywords/UniqueItemsKeyword.php b/vendor/opis/json-schema/src/Keywords/UniqueItemsKeyword.php new file mode 100644 index 000000000..f952c76a7 --- /dev/null +++ b/vendor/opis/json-schema/src/Keywords/UniqueItemsKeyword.php @@ -0,0 +1,57 @@ +currentData(); + if (!$data) { + return null; + } + + $count = count($data); + + for ($i = 0; $i < $count - 1; $i++) { + for ($j = $i + 1; $j < $count; $j++) { + if (Helper::equals($data[$i], $data[$j])) { + return $this->error($schema, $context, 'uniqueItems', 'Array must have unique items', [ + 'duplicate' => $data[$i], + 'indexes' => [$i, $j], + ]); + } + } + } + + return null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/DataKeywordTrait.php b/vendor/opis/json-schema/src/Parsers/DataKeywordTrait.php new file mode 100644 index 000000000..44e0f4509 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/DataKeywordTrait.php @@ -0,0 +1,59 @@ +{'$data'}) || count(get_object_vars($value)) !== 1) { + return null; + } + + return JsonPointer::parse($value->{'$data'}); + } + + /** + * @param SchemaParser $parser + * @param string|null $keyword + * @return bool + */ + protected function isDataKeywordAllowed(SchemaParser $parser, ?string $keyword = null): bool + { + if (!($enabled = $parser->option('allowDataKeyword'))) { + return false; + } + + if ($enabled === true) { + return true; + } + + if ($keyword === null) { + return false; + } + + return is_array($enabled) && in_array($keyword, $enabled); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/DefaultVocabulary.php b/vendor/opis/json-schema/src/Parsers/DefaultVocabulary.php new file mode 100644 index 000000000..d05d4280c --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/DefaultVocabulary.php @@ -0,0 +1,56 @@ +getKeywordParsers(); + $keywordValidators = $this->getKeywordValidatorParsers(); + $pragmas = $this->getPragmaParsers(); + + if ($extraVocabulary) { + $keywords = array_merge($keywords, $extraVocabulary->keywords()); + $keywordValidators = array_merge($keywordValidators, $extraVocabulary->keywordValidators()); + $pragmas = array_merge($pragmas, $extraVocabulary->pragmas()); + } + + array_unshift($keywords, $this->getRefKeywordParser()); + + parent::__construct($keywords, $keywordValidators, $pragmas); + } + + /** + * @return string + */ + abstract public function version(): string; + + /** + * @return bool + */ + abstract public function allowKeywordsAlongsideRef(): bool; + + /** + * @return bool + */ + abstract public function supportsAnchorId(): bool; + + /** + * @return KeywordParser + */ + abstract protected function getRefKeywordParser(): KeywordParser; + + /** + * @return KeywordParser[] + */ + abstract protected function getKeywordParsers(): array; + + /** + * @return KeywordValidatorParser[] + */ + protected function getKeywordValidatorParsers(): array + { + return []; + } + + /** + * @return PragmaParser[] + */ + protected function getPragmaParsers(): array + { + return []; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/DraftOptionTrait.php b/vendor/opis/json-schema/src/Parsers/DraftOptionTrait.php new file mode 100644 index 000000000..32014b4fc --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/DraftOptionTrait.php @@ -0,0 +1,43 @@ +option($option); + + if (!$value) { + return false; + } + + if ($value === true) { + return true; + } + + if (is_array($value)) { + return in_array($info->draft(), $value); + } + + return $value === $info->draft(); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Drafts/Draft06.php b/vendor/opis/json-schema/src/Parsers/Drafts/Draft06.php new file mode 100644 index 000000000..f8a48343a --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Drafts/Draft06.php @@ -0,0 +1,145 @@ + '$recursiveRef', 'anchor' => '$recursiveAnchor', 'fragment' => false], + ]); + } + + /** + * @inheritDoc + */ + protected function getKeywordParsers(): array + { + return [ + // Generic + new TypeKeywordParser('type'), + new ConstKeywordParser('const'), + new EnumKeywordParser('enum'), + new FormatKeywordParser('format'), + + // String + new MinLengthKeywordParser('minLength'), + new MaxLengthKeywordParser('maxLength'), + new PatternKeywordParser("pattern"), + new ContentEncodingKeywordParser('contentEncoding'), + new ContentMediaTypeKeywordParser('contentMediaType'), + new ContentSchemaKeywordParser('contentSchema'), + + // Number + new MinimumKeywordParser('minimum', 'exclusiveMinimum'), + new MaximumKeywordParser('maximum', 'exclusiveMaximum'), + new ExclusiveMinimumKeywordParser('exclusiveMinimum'), + new ExclusiveMaximumKeywordParser('exclusiveMaximum'), + new MultipleOfKeywordParser('multipleOf'), + + // Array + new MinItemsKeywordParser('minItems'), + new MaxItemsKeywordParser('maxItems'), + new UniqueItemsKeywordParser('uniqueItems'), + new ContainsKeywordParser('contains', 'minContains', 'maxContains'), + new ItemsKeywordParser('items'), + new AdditionalItemsKeywordParser('additionalItems'), + + // Object + new MinPropertiesKeywordParser('minProperties'), + new MaxPropertiesKeywordParser('maxProperties'), + new RequiredKeywordParser('required'), + new DependenciesKeywordParser('dependencies'), // keep for draft-07 compatibility + new DependentRequiredKeywordParser('dependentRequired'), + new DependentSchemasKeywordParser('dependentSchemas'), + new PropertyNamesKeywordParser('propertyNames'), + new PropertiesKeywordParser('properties'), + new PatternPropertiesKeywordParser('patternProperties'), + new AdditionalPropertiesKeywordParser('additionalProperties'), + + // Conditionals + new IfThenElseKeywordParser('if', 'then', 'else'), + new AnyOfKeywordParser('anyOf'), + new AllOfKeywordParser('allOf'), + new OneOfKeywordParser('oneOf'), + new NotKeywordParser('not'), + + // Unevaluated + new UnevaluatedPropertiesKeywordParser('unevaluatedProperties'), + new UnevaluatedItemsKeywordParser('unevaluatedItems'), + + // Optional + new DefaultKeywordParser('default'), + ]; + } + +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Drafts/Draft202012.php b/vendor/opis/json-schema/src/Parsers/Drafts/Draft202012.php new file mode 100644 index 000000000..b4e960ff2 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Drafts/Draft202012.php @@ -0,0 +1,163 @@ + '$dynamicRef', 'anchor' => '$dynamicAnchor', 'fragment' => true], + ['ref' => '$recursiveRef', 'anchor' => '$recursiveAnchor', 'fragment' => false], + ]); + } + + /** + * @inheritDoc + */ + protected function getKeywordParsers(): array + { + return [ + // Generic + new TypeKeywordParser('type'), + new ConstKeywordParser('const'), + new EnumKeywordParser('enum'), + new FormatKeywordParser('format'), + + // String + new MinLengthKeywordParser('minLength'), + new MaxLengthKeywordParser('maxLength'), + new PatternKeywordParser("pattern"), + new ContentEncodingKeywordParser('contentEncoding'), + new ContentMediaTypeKeywordParser('contentMediaType'), + new ContentSchemaKeywordParser('contentSchema'), + + // Number + new MinimumKeywordParser('minimum', 'exclusiveMinimum'), + new MaximumKeywordParser('maximum', 'exclusiveMaximum'), + new ExclusiveMinimumKeywordParser('exclusiveMinimum'), + new ExclusiveMaximumKeywordParser('exclusiveMaximum'), + new MultipleOfKeywordParser('multipleOf'), + + // Array + new MinItemsKeywordParser('minItems'), + new MaxItemsKeywordParser('maxItems'), + new UniqueItemsKeywordParser('uniqueItems'), + new ContainsKeywordParser('contains', 'minContains', 'maxContains'), + new ItemsKeywordParser('prefixItems', ItemsKeywordParser::ONLY_ARRAY), + new ItemsKeywordParser('items', ItemsKeywordParser::ONLY_SCHEMA, 'prefixItems'), + // keep for draft-2019-09 compatibility + new AdditionalItemsKeywordParser('additionalItems'), + + // Object + new MinPropertiesKeywordParser('minProperties'), + new MaxPropertiesKeywordParser('maxProperties'), + new RequiredKeywordParser('required'), + new DependenciesKeywordParser('dependencies'), // keep for draft-07 compatibility + new DependentRequiredKeywordParser('dependentRequired'), + new DependentSchemasKeywordParser('dependentSchemas'), + new PropertyNamesKeywordParser('propertyNames'), + new PropertiesKeywordParser('properties'), + new PatternPropertiesKeywordParser('patternProperties'), + new AdditionalPropertiesKeywordParser('additionalProperties'), + + // Conditionals + new IfThenElseKeywordParser('if', 'then', 'else'), + new AnyOfKeywordParser('anyOf'), + new AllOfKeywordParser('allOf'), + new OneOfKeywordParser('oneOf'), + new NotKeywordParser('not'), + + // Unevaluated + new UnevaluatedPropertiesKeywordParser('unevaluatedProperties'), + new UnevaluatedItemsKeywordParser('unevaluatedItems'), + + // Optional + new DefaultKeywordParser('default'), + ]; + } + +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/KeywordParser.php b/vendor/opis/json-schema/src/Parsers/KeywordParser.php new file mode 100644 index 000000000..4a49a247d --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/KeywordParser.php @@ -0,0 +1,62 @@ +draft(); + return $draft !== '06' && $draft !== '07'; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/KeywordParserTrait.php b/vendor/opis/json-schema/src/Parsers/KeywordParserTrait.php new file mode 100644 index 000000000..b604afcba --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/KeywordParserTrait.php @@ -0,0 +1,76 @@ +keyword = $keyword; + } + + /** + * @param object|SchemaInfo $schema + * @param string|null $keyword + * @return bool + */ + protected function keywordExists(object $schema, ?string $keyword = null): bool + { + if ($schema instanceof SchemaInfo) { + $schema = $schema->data(); + } + + return property_exists($schema, $keyword ?? $this->keyword); + } + + /** + * @param object|SchemaInfo $schema + * @param string|null $keyword + * @return mixed + */ + protected function keywordValue(object $schema, ?string $keyword = null) + { + if ($schema instanceof SchemaInfo) { + $schema = $schema->data(); + } + + return $schema->{$keyword ?? $this->keyword}; + } + + /** + * @param string $message + * @param SchemaInfo $info + * @param string|null $keyword + * @return InvalidKeywordException + */ + protected function keywordException(string $message, SchemaInfo $info, ?string $keyword = null): InvalidKeywordException + { + $keyword = $keyword ?? $this->keyword; + + return new InvalidKeywordException(str_replace('{keyword}', $keyword, $message), $keyword, $info); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/KeywordValidatorParser.php b/vendor/opis/json-schema/src/Parsers/KeywordValidatorParser.php new file mode 100644 index 000000000..19e43240e --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/KeywordValidatorParser.php @@ -0,0 +1,34 @@ +option('allowPragmas') || !$this->keywordExists($info)) { + return null; + } + + $value = $this->keywordValue($info); + + if (!is_object($value)) { + throw $this->keywordException('{keyword} must be an object', $info); + } + + $list = []; + + $draft = $info->draft() ?? $parser->defaultDraftVersion(); + + $pragmaInfo = new SchemaInfo($value, null, $info->id() ?? $info->base(), $info->root(), + array_merge($info->path(), [$this->keyword]), $draft); + + foreach ($parser->draft($draft)->pragmas() as $pragma) { + if ($handler = $pragma->parse($pragmaInfo, $parser, $shared)) { + $list[] = $handler; + } + } + + return $list ? new PragmaKeywordValidator($list) : null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/AdditionalItemsKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/AdditionalItemsKeywordParser.php new file mode 100644 index 000000000..ceb0c82b2 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/AdditionalItemsKeywordParser.php @@ -0,0 +1,63 @@ +option('keepAdditionalItemsKeyword') && $info->draft() === '2020-12') { + return null; + } + + $schema = $info->data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + if (!property_exists($schema, 'items') || !is_array($schema->items)) { + // Ignore additionalItems + return null; + } + + $value = $this->keywordValue($schema); + + if (!is_bool($value) && !is_object($value)) { + throw $this->keywordException("{keyword} must be a json schema (object or boolean)", $info); + } + + return new AdditionalItemsKeyword($value, count($schema->items)); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/AdditionalPropertiesKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/AdditionalPropertiesKeywordParser.php new file mode 100644 index 000000000..931cd3fd6 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/AdditionalPropertiesKeywordParser.php @@ -0,0 +1,54 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if (!is_bool($value) && !is_object($value)) { + throw $this->keywordException("{keyword} must be a json schema (object or boolean)", $info); + } + + return new AdditionalPropertiesKeyword($value); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/AllOfKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/AllOfKeywordParser.php new file mode 100644 index 000000000..8831826c0 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/AllOfKeywordParser.php @@ -0,0 +1,75 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if (!is_array($value)) { + throw $this->keywordException("{keyword} should be an array of json schemas", $info); + } + + if (!$value) { + throw $this->keywordException("{keyword} must have at least one element", $info); + } + + $valid = 0; + + foreach ($value as $index => $item) { + if ($item === false) { + throw $this->keywordException("{keyword} contains false schema", $info); + } + if ($item === true) { + $valid++; + continue; + } + if (!is_object($item)) { + throw $this->keywordException("{keyword}[{$index}] must be a json schema", $info); + } elseif (!count(get_object_vars($item))) { + $valid++; + } + } + + return $valid !== count($value) ? new AllOfKeyword($value) : null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/AnyOfKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/AnyOfKeywordParser.php new file mode 100644 index 000000000..5dd76a45f --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/AnyOfKeywordParser.php @@ -0,0 +1,75 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if (!is_array($value)) { + throw $this->keywordException("{keyword} should be an array of json schemas", $info); + } + + if (!$value) { + throw $this->keywordException("{keyword} must have at least one element", $info); + } + + $alwaysValid = false; + + foreach ($value as $index => $item) { + if ($item === true) { + $alwaysValid = true; + continue; + } + if ($item === false) { + continue; + } + if (!is_object($item)) { + throw $this->keywordException("{keyword}[{$index}] must be a json schema", $info); + } elseif (!count(get_object_vars($item))) { + $alwaysValid = true; + } + } + + return new AnyOfKeyword($value, $alwaysValid); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/ConstKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/ConstKeywordParser.php new file mode 100644 index 000000000..f15cd158e --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/ConstKeywordParser.php @@ -0,0 +1,74 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if ($this->isDataKeywordAllowed($parser, $this->keyword)) { + if ($pointer = $this->getDataKeywordPointer($value)) { + return new ConstDataKeyword($pointer); + } + } + + $type = Helper::getJsonType($value); + if ($type === null) { + throw $this->keywordException("{keyword} contains unknown json data type", $info); + } + + if (isset($shared->types)) { + if (!Helper::jsonTypeMatches($type, $shared->types)) { + throw $this->keywordException("{keyword} contains a value that doesn't match the type keyword", $info); + } + } else { + $shared->types = [$type]; + } + + return new ConstKeyword($value); + } + +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/ContainsKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/ContainsKeywordParser.php new file mode 100644 index 000000000..e6e1c2d07 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/ContainsKeywordParser.php @@ -0,0 +1,85 @@ +minContains = $minContains; + $this->maxContains = $maxContains; + } + + /** + * @inheritDoc + */ + public function type(): string + { + return self::TYPE_ARRAY; + } + + /** + * @inheritDoc + */ + public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword + { + $schema = $info->data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if (!is_bool($value) && !is_object($value)) { + throw $this->keywordException("{keyword} must be a json schema (object or boolean)", $info); + } + + $min = $max = null; + + if ($this->minContains && $this->keywordExists($schema, $this->minContains)) { + $min = $this->keywordValue($schema, $this->minContains); + if (!is_int($min) || $min < 0) { + throw $this->keywordException("{keyword} must be a non-negative integer", $info, $this->minContains); + } + } + + if ($this->maxContains && $this->keywordExists($schema, $this->maxContains)) { + $max = $this->keywordValue($schema, $this->maxContains); + if (!is_int($max) || $max < 0) { + throw $this->keywordException("{keyword} must be a non-negative integer", $info, $this->maxContains); + } + if ($min !== null && $max < $min) { + throw $this->keywordException("{keyword} must be greater than {$this->minContains}", $info, $this->maxContains); + } + } elseif ($min === 0) { + return null; + } + + return new ContainsKeyword($value, $min, $max); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/ContentEncodingKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/ContentEncodingKeywordParser.php new file mode 100644 index 000000000..992019bcd --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/ContentEncodingKeywordParser.php @@ -0,0 +1,62 @@ +optionAllowedForDraft('decodeContent', $info, $parser)) { + return null; + } + + $schema = $info->data(); + + $resolver = $parser->getContentEncodingResolver(); + + if (!$resolver || !$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if (!is_string($value)) { + throw $this->keywordException("{keyword} must be a string", $info); + } + + return new ContentEncodingKeyword(strtolower($value), $resolver); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/ContentMediaTypeKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/ContentMediaTypeKeywordParser.php new file mode 100644 index 000000000..97c5cacd3 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/ContentMediaTypeKeywordParser.php @@ -0,0 +1,62 @@ +optionAllowedForDraft('decodeContent', $info, $parser)) { + return null; + } + + $schema = $info->data(); + + $resolver = $parser->getMediaTypeResolver(); + + if (!$resolver || !$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if (!is_string($value)) { + throw $this->keywordException("{keyword} must be a string", $info); + } + + return new ContentMediaTypeKeyword($value, $resolver); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/ContentSchemaKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/ContentSchemaKeywordParser.php new file mode 100644 index 000000000..58cce2fbc --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/ContentSchemaKeywordParser.php @@ -0,0 +1,60 @@ +optionAllowedForDraft('decodeContent', $info, $parser)) { + return null; + } + + $schema = $info->data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if (!is_object($value)) { + throw $this->keywordException("{keyword} must be a valid json schema object", $info); + } + + return new ContentSchemaKeyword($value); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/DefaultKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/DefaultKeywordParser.php new file mode 100644 index 000000000..bf13a6f01 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/DefaultKeywordParser.php @@ -0,0 +1,85 @@ +properties = $properties; + } + + /** + * @inheritDoc + */ + public function type(): string + { + return self::TYPE_APPEND; + } + + /** + * @inheritDoc + */ + public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword + { + $schema = $info->data(); + + if (!$parser->option('allowDefaults')) { + return null; + } + + $defaults = null; + + if ($this->keywordExists($schema)) { + $defaults = $this->keywordValue($schema); + + if (is_object($defaults)) { + $defaults = (array)Helper::cloneValue($defaults); + } else { + $defaults = null; + } + } + + if ($this->properties !== null && property_exists($schema, $this->properties) + && is_object($schema->{$this->properties})) { + foreach ($schema->{$this->properties} as $name => $value) { + if (is_object($value) && property_exists($value, $this->keyword)) { + $defaults[$name] = $value->{$this->keyword}; + } + } + } + + if (!$defaults) { + return null; + } + + return new DefaultKeyword($defaults); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/DependenciesKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/DependenciesKeywordParser.php new file mode 100644 index 000000000..078a3b9e8 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/DependenciesKeywordParser.php @@ -0,0 +1,80 @@ +option('keepDependenciesKeyword') && !in_array($info->draft(), ['06', '07'])) { + return null; + } + + $schema = $info->data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + if (!is_object($value)) { + throw $this->keywordException("{keyword} must be an object", $info); + } + + $list = get_object_vars($value); + + foreach ($list as $name => $s) { + if (is_array($s)) { + if (!$s) { + unset($list[$name]); + continue; + } + foreach ($s as $p) { + if (!is_string($p)) { + throw $this->keywordException("{keyword} must be an object containing json schemas or arrays of property names", $info); + } + } + $list[$name] = array_unique($s); + } elseif (is_bool($s)) { + if ($s) { + unset($list[$name]); + } + } elseif (!is_object($s)) { + throw $this->keywordException("{keyword} must be an object containing json schemas or arrays of property names", $info); + } + } + + return $list ? new DependenciesKeyword($list) : null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/DependentRequiredKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/DependentRequiredKeywordParser.php new file mode 100644 index 000000000..d8f4289f2 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/DependentRequiredKeywordParser.php @@ -0,0 +1,70 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + if (!is_object($value)) { + throw $this->keywordException("{keyword} must be an object", $info); + } + + $list = []; + foreach ($value as $name => $s) { + if (!is_array($s)) { + throw $this->keywordException("{keyword} must be an object containing json schemas or arrays of property names", $info); + } + if (!$s) { + // Empty array + continue; + } + foreach ($s as $p) { + if (!is_string($p)) { + throw $this->keywordException("{keyword} must be an object containing arrays of property names", $info); + } + } + $list[$name] = array_unique($s); + } + + return $list ? new DependentRequiredKeyword($list) : null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/DependentSchemasKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/DependentSchemasKeywordParser.php new file mode 100644 index 000000000..4fa313af3 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/DependentSchemasKeywordParser.php @@ -0,0 +1,73 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + if (!is_object($value)) { + throw $this->keywordException("{keyword} must be an object", $info); + } + + $valid = 0; + $total = 0; + + foreach ($value as $name => $s) { + $total++; + if (is_bool($s)) { + if ($s) { + $valid++; + } + } elseif (!is_object($s)) { + throw $this->keywordException("{keyword} must be an object containing json schemas", $info); + } elseif (!count(get_object_vars($s))) { + $valid++; + } + } + + if (!$total) { + return null; + } + + return $valid !== $total ? new DependentSchemasKeyword($value) : null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/EnumKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/EnumKeywordParser.php new file mode 100644 index 000000000..e666c9eed --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/EnumKeywordParser.php @@ -0,0 +1,107 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if ($this->isDataKeywordAllowed($parser, $this->keyword)) { + if ($pointer = $this->getDataKeywordPointer($value)) { + return new EnumDataKeyword($pointer); + } + } + + if (!is_array($value) || !$value) { + throw $this->keywordException("{keyword} must be a non-empty array", $info); + } + + $hasConst = property_exists($schema, 'const'); + $constMatched = false; + + $allowedTypes = isset($shared->types) ? $shared->types : null; + $foundTypes = []; + $list = []; + foreach ($value as $item) { + $type = Helper::getJsonType($item); + if ($type === null) { + throw $this->keywordException("{keyword} contains invalid json data type", $info); + } + + if ($allowedTypes && !Helper::jsonTypeMatches($type, $allowedTypes)) { + continue; + } + + if ($hasConst && Helper::equals($item, $schema->const)) { + $constMatched = true; + break; + } + + if (!in_array($type, $foundTypes)) { + $foundTypes[] = $type; + } + + $list[] = $item; + } + + if ($hasConst) { + if ($constMatched) { + return null; + } + throw $this->keywordException("{keyword} does not contain the value of const keyword", $info); + } + + if ($foundTypes) { + if ($allowedTypes === null) { + $shared->types = $foundTypes; + } else { + $shared->types = array_unique(array_merge($shared->types, $foundTypes)); + } + } + + return new EnumKeyword($list); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/ExclusiveMaximumKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/ExclusiveMaximumKeywordParser.php new file mode 100644 index 000000000..afe8f7860 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/ExclusiveMaximumKeywordParser.php @@ -0,0 +1,70 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if (is_bool($value) && $parser->option('allowExclusiveMinMaxAsBool')) { + return null; + } + + if ($this->isDataKeywordAllowed($parser, $this->keyword)) { + if ($pointer = $this->getDataKeywordPointer($value)) { + return new ExclusiveMaximumDataKeyword($pointer); + } + } + + if (!is_int($value) && !is_float($value) || is_nan($value) || !is_finite($value)) { + throw $this->keywordException('{keyword} must contain a valid number', $info); + } + + return new ExclusiveMaximumKeyword($value); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/ExclusiveMinimumKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/ExclusiveMinimumKeywordParser.php new file mode 100644 index 000000000..540d62d4f --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/ExclusiveMinimumKeywordParser.php @@ -0,0 +1,70 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if (is_bool($value) && $parser->option('allowExclusiveMinMaxAsBool')) { + return null; + } + + if ($this->isDataKeywordAllowed($parser, $this->keyword)) { + if ($pointer = $this->getDataKeywordPointer($value)) { + return new ExclusiveMinimumDataKeyword($pointer); + } + } + + if (!is_int($value) && !is_float($value) || is_nan($value) || !is_finite($value)) { + throw $this->keywordException('{keyword} must contain a valid number', $info); + } + + return new ExclusiveMinimumKeyword($value); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/FiltersKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/FiltersKeywordParser.php new file mode 100644 index 000000000..2b8ced838 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/FiltersKeywordParser.php @@ -0,0 +1,161 @@ +data(); + + if (!$parser->option('allowFilters')) { + return null; + } + + $resolver = $parser->getFilterResolver(); + + if (!$resolver || !$this->keywordExists($schema)) { + return null; + } + + $filters = $this->parseFilters($parser, $resolver, $this->keywordValue($schema), $info); + if (!$filters) { + return null; + } + + return new FiltersKeyword($filters); + } + + /** + * @param SchemaParser $parser + * @param FilterResolver $filterResolver + * @param mixed $filters + * @param SchemaInfo $info + * @return array|null + */ + protected function parseFilters( + SchemaParser $parser, + FilterResolver $filterResolver, + $filters, + SchemaInfo $info + ): ?array + { + if (is_string($filters)) { + if ($filters = $this->parseFilter($parser, $filterResolver, $filters, $info)) { + return [$filters]; + } + + return null; + } + + if (is_object($filters)) { + if ($filter = $this->parseFilter($parser, $filterResolver, $filters, $info)) { + return [$filter]; + } + + return null; + } + + if (is_array($filters)) { + if (!$filters) { + return null; + } + $list = []; + foreach ($filters as $filter) { + if ($filter = $this->parseFilter($parser, $filterResolver, $filter, $info)) { + $list[] = $filter; + } + } + + return $list ?: null; + } + + throw $this->keywordException('{keyword} can be a non-empty string, an object or an array of string and objects', $info); + } + + /** + * @param SchemaParser $parser + * @param FilterResolver $resolver + * @param $filter + * @param SchemaInfo $info + * @return object|null + */ + protected function parseFilter( + SchemaParser $parser, + FilterResolver $resolver, + $filter, + SchemaInfo $info + ): ?object + { + $vars = null; + if (is_object($filter)) { + if (!property_exists($filter, '$func') || !is_string($filter->{'$func'}) || $filter->{'$func'} === '') { + throw $this->keywordException('$func (for {keyword}) must be a non-empty string', $info); + } + + $vars = get_object_vars($filter); + unset($vars['$func']); + + if (property_exists($filter, '$vars')) { + if (!is_object($filter->{'$vars'})) { + throw $this->keywordException('$vars (for {keyword}) must be an object', $info); + } + unset($vars['$vars']); + $vars = get_object_vars($filter->{'$vars'}) + $vars; + } + + $filter = $filter->{'$func'}; + } elseif (!is_string($filter) || $filter === '') { + throw $this->keywordException('{keyword} can be a non-empty string, an object or an array of string and objects', $info); + } + + $list = $resolver->resolveAll($filter); + if (!$list) { + throw $this->keywordException("{keyword}: {$filter} doesn't exists", $info); + } + + $list = $this->resolveSubTypes($list); + + return (object)[ + 'name' => $filter, + 'args' => $vars ? $this->createVariables($parser, $vars) : null, + 'types' => $list, + ]; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/FormatKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/FormatKeywordParser.php new file mode 100644 index 000000000..b3c02622f --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/FormatKeywordParser.php @@ -0,0 +1,71 @@ +data(); + + $resolver = $parser->getFormatResolver(); + + if (!$resolver || !$parser->option('allowFormats') || !$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if ($this->isDataKeywordAllowed($parser, $this->keyword)) { + if ($pointer = $this->getDataKeywordPointer($value)) { + return new FormatDataKeyword($pointer, $resolver); + } + } + + if (!is_string($value)) { + throw $this->keywordException("{keyword} must be a string", $info); + } + + $list = $resolver->resolveAll($value); + + if (!$list) { + return null; + } + + return new FormatKeyword($value, $this->resolveSubTypes($list)); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/IfThenElseKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/IfThenElseKeywordParser.php new file mode 100644 index 000000000..7ba50012f --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/IfThenElseKeywordParser.php @@ -0,0 +1,109 @@ +then = $then; + $this->else = $else; + } + + /** + * @inheritDoc + */ + public function type(): string + { + return self::TYPE_AFTER; + } + + /** + * @inheritDoc + */ + public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword + { + $schema = $info->data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $if = $this->keywordValue($schema); + if (!$this->isJsonSchema($if)) { + throw $this->keywordException("{keyword} keyword must be a json schema", $info); + } + + $then = true; + if (property_exists($schema, $this->then)) { + $then = $schema->{$this->then}; + } + if (!$this->isJsonSchema($then)) { + throw $this->keywordException("{keyword} keyword must be a json schema", $info, $this->then); + } + + $else = true; + if (property_exists($schema, $this->else)) { + $else = $schema->{$this->else}; + } + if (!$this->isJsonSchema($else)) { + throw $this->keywordException("{keyword} keyword must be a json schema", $info, $this->else); + } + + if ($if === true) { + if ($then === true) { + return null; + } + $else = true; + } elseif ($if === false) { + if ($else === true) { + return null; + } + $then = true; + } elseif ($then === true && $else === true) { + return null; + } + + return new IfThenElseKeyword($if, $then, $else); + } + + /** + * @param $value + * @return bool + */ + protected function isJsonSchema($value): bool + { + return is_bool($value) || is_object($value); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/ItemsKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/ItemsKeywordParser.php new file mode 100644 index 000000000..697ec6027 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/ItemsKeywordParser.php @@ -0,0 +1,117 @@ +mode = $mode; + $this->startIndexKeyword = $startIndexKeyword; + } + + /** + * @inheritDoc + */ + public function type(): string + { + return self::TYPE_ARRAY; + } + + /** + * @inheritDoc + */ + public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword + { + $schema = $info->data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + $alwaysValid = false; + + if (is_bool($value)) { + if ($this->mode === self::ONLY_ARRAY) { + throw $this->keywordException("{keyword} must contain an array of json schemas", $info); + } + if ($value) { + $alwaysValid = true; + } + } elseif (is_array($value)) { + if ($this->mode === self::ONLY_SCHEMA) { + throw $this->keywordException("{keyword} must contain a valid json schema", $info); + } + $valid = 0; + foreach ($value as $index => $v) { + if (is_bool($v)) { + if ($v) { + $valid++; + } + } elseif (!is_object($v)) { + throw $this->keywordException("{keyword}[$index] must contain a valid json schema", $info); + } elseif (!count(get_object_vars($v))) { + $valid++; + } + } + if ($valid === count($value)) { + $alwaysValid = true; + } + } elseif (!is_object($value)) { + if ($this->mode === self::BOTH) { + throw $this->keywordException("{keyword} must be a json schema or an array of json schemas", $info); + } elseif ($this->mode === self::ONLY_ARRAY) { + throw $this->keywordException("{keyword} must contain an array of json schemas", $info); + } else { + throw $this->keywordException("{keyword} must contain a valid json schema", $info); + } + } else { + if ($this->mode === self::ONLY_ARRAY) { + throw $this->keywordException("{keyword} must contain an array of json schemas", $info); + } + if (!count(get_object_vars($value))) { + $alwaysValid = true; + } + } + + $startIndex = 0; + if ($this->startIndexKeyword !== null && $this->keywordExists($schema, $this->startIndexKeyword)) { + $start = $this->keywordValue($schema, $this->startIndexKeyword); + if (is_array($start)) { + $startIndex = count($start); + } + } + + return new ItemsKeyword($value, $alwaysValid, $this->keyword, $startIndex); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/MaxItemsKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/MaxItemsKeywordParser.php new file mode 100644 index 000000000..e24fa7504 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/MaxItemsKeywordParser.php @@ -0,0 +1,63 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if ($this->isDataKeywordAllowed($parser, $this->keyword)) { + if ($pointer = $this->getDataKeywordPointer($value)) { + return new MaxItemsDataKeyword($pointer); + } + } + + if (!is_int($value) || $value < 0) { + throw $this->keywordException("{keyword} most be a positive integer", $info); + } + + return new MaxItemsKeyword($value); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/MaxLengthKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/MaxLengthKeywordParser.php new file mode 100644 index 000000000..54bc88b75 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/MaxLengthKeywordParser.php @@ -0,0 +1,63 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if ($this->isDataKeywordAllowed($parser, $this->keyword)) { + if ($pointer = $this->getDataKeywordPointer($value)) { + return new MaxLengthDataKeyword($pointer); + } + } + + if (!is_int($value) || $value < 0) { + throw $this->keywordException("{keyword} must be a non-negative integer", $info); + } + + return new MaxLengthKeyword($value); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/MaxPropertiesKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/MaxPropertiesKeywordParser.php new file mode 100644 index 000000000..971d3c8b2 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/MaxPropertiesKeywordParser.php @@ -0,0 +1,63 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if ($this->isDataKeywordAllowed($parser, $this->keyword)) { + if ($pointer = $this->getDataKeywordPointer($value)) { + return new MaxPropertiesDataKeyword($pointer); + } + } + + if (!is_int($value) || $value < 0) { + throw $this->keywordException("{keyword} must be a non-negative integer", $info); + } + + return new MaxPropertiesKeywords($value); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/MaximumKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/MaximumKeywordParser.php new file mode 100644 index 000000000..1eb982408 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/MaximumKeywordParser.php @@ -0,0 +1,91 @@ +exclusiveKeyword = $exclusiveKeyword; + } + + /** + * @inheritDoc + */ + public function type(): string + { + return self::TYPE_NUMBER; + } + + /** + * @inheritDoc + */ + public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword + { + $schema = $info->data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + $exclusive = false; + if ($parser->option('allowExclusiveMinMaxAsBool') && + $this->exclusiveKeyword !== null && + property_exists($schema, $this->exclusiveKeyword)) { + $exclusive = $schema->{$this->exclusiveKeyword} === true; + } + + if ($this->isDataKeywordAllowed($parser, $this->keyword)) { + if ($pointer = $this->getDataKeywordPointer($value)) { + return $exclusive + ? new ExclusiveMaximumDataKeyword($pointer) + : new MaximumDataKeyword($pointer); + } + } + + if (!is_int($value) && !is_float($value) || is_nan($value) || !is_finite($value)) { + throw $this->keywordException('{keyword} must contain a valid number', $info); + } + + return $exclusive + ? new ExclusiveMaximumKeyword($value) + : new MaximumKeyword($value); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/MinItemsKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/MinItemsKeywordParser.php new file mode 100644 index 000000000..55b40aa3d --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/MinItemsKeywordParser.php @@ -0,0 +1,67 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if ($this->isDataKeywordAllowed($parser, $this->keyword)) { + if ($pointer = $this->getDataKeywordPointer($value)) { + return new MinItemsDataKeyword($pointer); + } + } + + if (!is_int($value) || $value < 0) { + throw $this->keywordException("{keyword} most be a positive integer", $info); + } + + if ($value === 0) { + return null; + } + + return new MinItemsKeyword($value); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/MinLengthKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/MinLengthKeywordParser.php new file mode 100644 index 000000000..0a4ab4e38 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/MinLengthKeywordParser.php @@ -0,0 +1,67 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if ($this->isDataKeywordAllowed($parser, $this->keyword)) { + if ($pointer = $this->getDataKeywordPointer($value)) { + return new MinLengthDataKeyword($pointer); + } + } + + if (!is_int($value) || $value < 0) { + throw $this->keywordException("{keyword} must be a non-negative integer", $info); + } + + if ($value === 0) { + return null; + } + + return new MinLengthKeyword($value); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/MinPropertiesKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/MinPropertiesKeywordParser.php new file mode 100644 index 000000000..c8f8928aa --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/MinPropertiesKeywordParser.php @@ -0,0 +1,67 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if ($this->isDataKeywordAllowed($parser, $this->keyword)) { + if ($pointer = $this->getDataKeywordPointer($value)) { + return new MinPropertiesDataKeyword($pointer); + } + } + + if (!is_int($value) || $value < 0) { + throw $this->keywordException("{keyword} must be a non-negative integer", $info); + } + + if ($value === 0) { + return null; + } + + return new MinPropertiesKeyword($value); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/MinimumKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/MinimumKeywordParser.php new file mode 100644 index 000000000..640ca904b --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/MinimumKeywordParser.php @@ -0,0 +1,91 @@ +exclusiveKeyword = $exclusiveKeyword; + } + + /** + * @inheritDoc + */ + public function type(): string + { + return self::TYPE_NUMBER; + } + + /** + * @inheritDoc + */ + public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword + { + $schema = $info->data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + $exclusive = false; + if ($parser->option('allowExclusiveMinMaxAsBool') && + $this->exclusiveKeyword !== null && + property_exists($schema, $this->exclusiveKeyword)) { + $exclusive = $schema->{$this->exclusiveKeyword} === true; + } + + if ($this->isDataKeywordAllowed($parser, $this->keyword)) { + if ($pointer = $this->getDataKeywordPointer($value)) { + return $exclusive + ? new ExclusiveMinimumDataKeyword($pointer) + : new MinimumDataKeyword($pointer); + } + } + + if (!is_int($value) && !is_float($value) || is_nan($value) || !is_finite($value)) { + throw $this->keywordException('{keyword} must contain a valid number', $info); + } + + return $exclusive + ? new ExclusiveMinimumKeyword($value) + : new MinimumKeyword($value); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/MultipleOfKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/MultipleOfKeywordParser.php new file mode 100644 index 000000000..8281d47bc --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/MultipleOfKeywordParser.php @@ -0,0 +1,67 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if ($this->isDataKeywordAllowed($parser, $this->keyword)) { + if ($pointer = $this->getDataKeywordPointer($value)) { + return new MultipleOfDataKeyword($pointer); + } + } + + if (!is_int($value) && !is_float($value) || is_nan($value) || !is_finite($value)) { + throw $this->keywordException("{keyword} must be a valid number (integer or float)", $info); + } + + if ($value <= 0) { + throw $this->keywordException("{keyword} must be greater than zero", $info); + } + + return new MultipleOfKeyword($value); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/NotKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/NotKeywordParser.php new file mode 100644 index 000000000..53bc5770c --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/NotKeywordParser.php @@ -0,0 +1,58 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if (is_bool($value)) { + if (!$value) { + return null; + } + } elseif (!is_object($value)) { + throw $this->keywordException("{keyword} must contain a json schema (object or boolean)", $info); + } + + return new NotKeyword($value); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/OneOfKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/OneOfKeywordParser.php new file mode 100644 index 000000000..66e0693c2 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/OneOfKeywordParser.php @@ -0,0 +1,79 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if (!is_array($value)) { + throw $this->keywordException("{keyword} should be an array of json schemas", $info); + } + + if (!$value) { + throw $this->keywordException("{keyword} must have at least one element", $info); + } + + $valid = 0; + + foreach ($value as $index => $item) { + if ($item === false) { + continue; + } + if ($item === true) { + if (++$valid > 1) { + throw $this->keywordException("{keyword} contains multiple true values", $info); + } + continue; + } + if (!is_object($item)) { + throw $this->keywordException("{keyword}[{$index}] must be a json schema", $info); + } elseif (!count(get_object_vars($item))) { + if (++$valid > 1) { + throw $this->keywordException("{keyword} contains multiple true values", $info); + } + } + } + + return new OneOfKeyword($value); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/PatternKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/PatternKeywordParser.php new file mode 100644 index 000000000..bae54661b --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/PatternKeywordParser.php @@ -0,0 +1,67 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if ($this->isDataKeywordAllowed($parser, $this->keyword)) { + if ($pointer = $this->getDataKeywordPointer($value)) { + return new PatternDataKeyword($pointer); + } + } + + if (!is_string($value)) { + throw $this->keywordException("{keyword} value must be a string", $info); + } + + if (!Helper::isValidPattern($value)) { + throw $this->keywordException("{keyword} value must be a valid regex", $info); + } + + return new PatternKeyword($value); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/PatternPropertiesKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/PatternPropertiesKeywordParser.php new file mode 100644 index 000000000..70f108525 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/PatternPropertiesKeywordParser.php @@ -0,0 +1,67 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + if (!is_object($value)) { + throw $this->keywordException("{keyword} must be an object", $info); + } + + $list = []; + + foreach ($value as $pattern => $item) { + if (!Helper::isValidPattern($pattern)) { + throw $this->keywordException("Each property name from {keyword} must be valid regex", $info); + } + + if (!is_bool($item) && !is_object($item)) { + throw $this->keywordException("{keyword}[{$pattern}] must be a json schema (object or boolean)", $info); + } + + $list[$pattern] = $item; + } + + return $list ? new PatternPropertiesKeyword($list) : null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/PropertiesKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/PropertiesKeywordParser.php new file mode 100644 index 000000000..9bdf76bf6 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/PropertiesKeywordParser.php @@ -0,0 +1,64 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if (!is_object($value)) { + throw $this->keywordException("{keyword} must be an object", $info); + } + + $list = []; + + foreach ($value as $name => $s) { + if (!is_bool($s) && !is_object($s)) { + throw $this->keywordException("{keyword}[{$name}] must be a json schema (object or boolean)", $info); + } + + $list[$name] = $s; + } + + return $list ? new PropertiesKeyword($list) : null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/PropertyNamesKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/PropertyNamesKeywordParser.php new file mode 100644 index 000000000..d8f3b2dac --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/PropertyNamesKeywordParser.php @@ -0,0 +1,58 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if (is_bool($value)) { + if ($value) { + return null; + } + } elseif (!is_object($value)) { + throw $this->keywordException("{keyword} must be a valid json schema (object or boolean)", $info); + } + + return new PropertyNamesKeyword($value); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/RefKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/RefKeywordParser.php new file mode 100644 index 000000000..5c1485697 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/RefKeywordParser.php @@ -0,0 +1,228 @@ +variations = $variations; + } + + /** + * @inheritDoc + */ + public function type(): string + { + return self::TYPE_AFTER_REF; + } + + /** + * @inheritDoc + */ + public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword + { + $ref = null; + $recursive = false; + $schema = $info->data(); + $variation = null; + + if ($this->keywordExists($schema)) { + $ref = $this->keywordValue($schema); + if (!is_string($ref) || $ref === '') { + throw $this->keywordException('{keyword} must be a non-empty string', $info); + } + } elseif ($this->variations) { + foreach ($this->variations as $v) { + if (!$this->keywordExists($schema, $v['ref'])) { + continue; + } + $ref = $this->keywordValue($schema, $v['ref']); + if ($v['fragment']) { + if (!preg_match('/^#[a-z][a-z0-9\\-.:_]*/i', $ref)) { + $this->keywordException("{keyword} value is malformed", $info, $v['ref']); + } + } elseif ($ref !== '#') { + $this->keywordException("{keyword} supports only '#' as value", $info, $v['ref']); + } + $variation = $v; + $recursive = true; + break; + } + if (!$recursive) { + return null; + } + } else { + return null; + } + + // Mappers + $mapper = null; + if ($parser->option('allowMappers') && property_exists($schema, '$map')) { + if (!is_object($schema->{'$map'}) && !is_array($schema->{'$map'})) { + throw $this->keywordException('$map keyword must be an object or an array', $info, '$map'); + } + + if (!empty($schema->{'$map'})) { + $mapper = $this->createVariables($parser, $schema->{'$map'}); + } + } + + // Globals + $globals = null; + if ($parser->option('allowGlobals') && property_exists($schema, '$globals')) { + if (!is_object($schema->{'$globals'})) { + throw $this->keywordException('$globals keyword must be an object', $info, '$globals'); + } + + if (!empty($schema->{'$globals'})) { + $globals = $this->createVariables($parser, $schema->{'$globals'}); + } + } + + // Pass slots + $slots = null; + if ($parser->option('allowSlots') && property_exists($schema, '$inject')) { + $slots = $this->parseInjectedSlots($info, $parser, '$inject'); + } + + if ($recursive) { + $ref = $info->idBaseRoot()->resolveRef($ref); + if ($variation['fragment']) { + return new RecursiveRefKeyword($ref->resolveRef('#'), $mapper, $globals, $slots, + $variation['ref'], $variation['anchor'], $ref->fragment()); + } + return new RecursiveRefKeyword($ref, $mapper, $globals, $slots, + $variation['ref'], $variation['anchor'], true); + } + + if ($ref === '#') { + return new URIRefKeyword(Uri::merge('#', $info->idBaseRoot()), $mapper, $globals, $slots, $this->keyword); + } + + if ($parser->option('allowTemplates') && UriTemplate::isTemplate($ref)) { + $tpl = new UriTemplate($ref); + + if ($tpl->hasPlaceholders()) { + $vars = null; + + if (property_exists($schema, '$vars')) { + if (!is_object($schema->{'$vars'})) { + throw $this->keywordException('$vars keyword must be an object', $info, '$vars'); + } + + if (!empty($schema->{'$vars'})) { + $vars = $this->createVariables($parser, $schema->{'$vars'}); + } + } + + return new TemplateRefKeyword( + $tpl, $vars, $mapper, + $globals, $slots, $this->keyword, + $parser->option('allowRelativeJsonPointerInRef') + ); + } + + unset($tpl); + } + + if ($ref[0] === '#') { + if (($pointer = JsonPointer::parse(substr($ref, 1))) && $pointer->isAbsolute()) { + return new PointerRefKeyword($pointer, $mapper, $globals, $slots, $this->keyword); + } + } elseif ($parser->option('allowRelativeJsonPointerInRef') && + ($pointer = JsonPointer::parse($ref)) && $pointer->isRelative()) { + return new PointerRefKeyword($pointer, $mapper, $globals, $slots, $this->keyword); + } + + $ref = Uri::merge($ref, $info->idBaseRoot(), true); + + if ($ref === null || !$ref->isAbsolute()) { + throw $this->keywordException('{keyword} must be a valid uri, uri-reference, uri-template or json-pointer', + $info); + } + + return new URIRefKeyword($ref, $mapper, $globals, $slots, $this->keyword); + } + + /** + * @param SchemaInfo $info + * @param SchemaParser $parser + * @param string $keyword + * @return string[]|object[]|Schema[] + */ + protected function parseInjectedSlots(SchemaInfo $info, SchemaParser $parser, string $keyword): ?array + { + $schema = $info->data(); + + if (!is_object($schema->{$keyword})) { + throw $this->keywordException('{keyword} keyword value must be an object', $info, $keyword); + } + + return $this->getSlotSchemas($info, $parser, $schema->{$keyword}, [$keyword]); + } + + /** + * @param SchemaInfo $info + * @param SchemaParser $parser + * @param object $slots + * @param array $path + * @return null + */ + protected function getSlotSchemas(SchemaInfo $info, SchemaParser $parser, object $slots, array $path): ?array + { + $keyword = null; + if ($path) { + $keyword = end($path); + $path = array_merge($info->path(), $path); + } else { + $path = $info->path(); + } + + $list = []; + + foreach ($slots as $name => $value) { + if ($value === null) { + continue; + } + if (is_string($value) || is_object($value)) { + $list[$name] = $value; + } elseif (is_bool($value)) { + $list[$name] = $parser->parseSchema(new SchemaInfo( + $value, null, $info->id() ?? $info->base(), $info->root(), + array_merge($path, [$name]), + $info->draft() ?? $parser->defaultDraftVersion() + )); + } else { + throw $this->keywordException('Slots must contain valid json schemas or slot names', $info, $keyword); + } + } + + return $list ?: null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/RequiredKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/RequiredKeywordParser.php new file mode 100644 index 000000000..b66557ea4 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/RequiredKeywordParser.php @@ -0,0 +1,105 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + $filter = $this->propertiesFilter($parser, $schema); + + if ($this->isDataKeywordAllowed($parser, $this->keyword)) { + if ($pointer = $this->getDataKeywordPointer($value)) { + return new RequiredDataKeyword($pointer, $filter); + } + } + + if (!is_array($value)) { + throw $this->keywordException("{keyword} must be an array of strings", $info); + } + + foreach ($value as $name) { + if (!is_string($name)) { + throw $this->keywordException("{keyword} must be an array of strings", $info); + } + } + + if ($filter) { + $value = array_filter($value, $filter); + } + + return $value ? new RequiredKeyword(array_unique($value)) : null; + } + + /** + * @param SchemaParser $parser + * @param object $schema + * @return callable|null + */ + protected function propertiesFilter(SchemaParser $parser, object $schema): ?callable + { + if (!$parser->option('allowDefaults')) { + return null; + } + + if (!property_exists($schema, 'properties') || !is_object($schema->properties)) { + return null; + } + + $props = $schema->properties; + + return static function (string $name) use ($props) { + if (!property_exists($props, $name)) { + return true; + } + + if (is_object($props->{$name}) && property_exists($props->{$name}, 'default')) { + return false; + } + + return true; + }; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/SlotsKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/SlotsKeywordParser.php new file mode 100644 index 000000000..176de51ef --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/SlotsKeywordParser.php @@ -0,0 +1,64 @@ +data(); + + if (!$parser->option('allowSlots') || !$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if (!is_object($value)) { + throw $this->keywordException('{keyword} keyword value must be an object', $info); + } + + $slots = []; + foreach ($value as $name => $fallback) { + if (!is_string($name) || $name === '') { + continue; + } + if (is_bool($fallback) || is_string($fallback) || is_object($fallback)) { + $slots[$name] = $fallback; + } + } + + return $slots ? new SlotsKeyword($slots) : null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/TypeKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/TypeKeywordParser.php new file mode 100644 index 000000000..764d5c925 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/TypeKeywordParser.php @@ -0,0 +1,78 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $type = $this->keywordValue($schema); + + if (is_string($type)) { + $type = [$type]; + } elseif (!is_array($type)) { + throw $this->keywordException('{keyword} can only be a string or an array of string', $info); + } + + foreach ($type as $t) { + if (!Helper::isValidJsonType($t)) { + throw $this->keywordException("{keyword} contains invalid json type: {$t}", $info); + } + } + + $type = array_unique($type); + + if (!isset($shared->types)) { + $shared->types = $type; + } else { + $shared->types = array_unique(array_merge($shared->types, $type)); + } + + $count = count($type); + + if ($count === 0) { + throw $this->keywordException("{keyword} cannot be an empty array", $info); + } elseif ($count === 1) { + $type = reset($type); + } + + return new TypeKeyword($type); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/UnevaluatedItemsKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/UnevaluatedItemsKeywordParser.php new file mode 100644 index 000000000..b60a02995 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/UnevaluatedItemsKeywordParser.php @@ -0,0 +1,73 @@ +data(); + + if (!$this->keywordExists($schema) || !$parser->option('allowUnevaluated')) { + return null; + } + +// if (!$this->makesSense($schema)) { +// return null; +// } + + $value = $this->keywordValue($schema); + + if (!is_bool($value) && !is_object($value)) { + throw $this->keywordException("{keyword} must be a json schema (object or boolean)", $info); + } + + return new UnevaluatedItemsKeyword($value); + } + + protected function makesSense(object $schema): bool + { + if (property_exists($schema, 'additionalItems')) { + return false; + } +// if (property_exists($schema, 'contains')) { +// return false; +// } + if (property_exists($schema, 'items') && !is_array($schema->items)) { + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/UnevaluatedPropertiesKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/UnevaluatedPropertiesKeywordParser.php new file mode 100644 index 000000000..4e3a22ef8 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/UnevaluatedPropertiesKeywordParser.php @@ -0,0 +1,68 @@ +data(); + + if (!$this->keywordExists($schema) || !$parser->option('allowUnevaluated')) { + return null; + } + +// if (!$this->makesSense($schema)) { +// return null; +// } + + $value = $this->keywordValue($schema); + + if (!is_bool($value) && !is_object($value)) { + throw $this->keywordException("{keyword} must be a json schema (object or boolean)", $info); + } + + return new UnevaluatedPropertiesKeyword($value); + } + + protected function makesSense(object $schema): bool + { + if (property_exists($schema, 'additionalProperties')) { + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Keywords/UniqueItemsKeywordParser.php b/vendor/opis/json-schema/src/Parsers/Keywords/UniqueItemsKeywordParser.php new file mode 100644 index 000000000..4712d6b67 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Keywords/UniqueItemsKeywordParser.php @@ -0,0 +1,63 @@ +data(); + + if (!$this->keywordExists($schema)) { + return null; + } + + $value = $this->keywordValue($schema); + + if ($this->isDataKeywordAllowed($parser, $this->keyword)) { + if ($pointer = $this->getDataKeywordPointer($value)) { + return new UniqueItemsDataKeyword($pointer); + } + } + + if (!is_bool($value)) { + throw $this->keywordException("{keyword} must be a boolean", $info); + } + + return $value ? new UniqueItemsKeyword() : null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/PragmaParser.php b/vendor/opis/json-schema/src/Parsers/PragmaParser.php new file mode 100644 index 000000000..1d20e6a2a --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/PragmaParser.php @@ -0,0 +1,84 @@ +pragma = $pragma; + } + + /** + * @param SchemaInfo $info + * @param SchemaParser $parser + * @param object $shared + * @return Pragma|null + */ + abstract public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Pragma; + + /** + * @param object|SchemaInfo $schema + * @param string|null $pragma + * @return bool + */ + protected function pragmaExists(object $schema, ?string $pragma = null): bool + { + if ($schema instanceof SchemaInfo) { + $schema = $schema->isObject() ? $schema->data() : null; + } + + return is_object($schema) && property_exists($schema, $pragma ?? $this->pragma); + } + + /** + * @param object|SchemaInfo $schema + * @param string|null $pragma + * @return mixed + */ + protected function pragmaValue(object $schema, ?string $pragma = null) + { + if ($schema instanceof SchemaInfo) { + $schema = $schema->isObject() ? $schema->data() : null; + } + + return is_object($schema) ? $schema->{$pragma ?? $this->pragma} : null; + } + + /** + * @param string $message + * @param SchemaInfo $info + * @param string|null $pragma + * @return InvalidPragmaException + */ + protected function pragmaException(string $message, SchemaInfo $info, ?string $pragma = null): InvalidPragmaException + { + $pragma = $pragma ?? $this->pragma; + + return new InvalidPragmaException(str_replace('{pragma}', $pragma, $message), $pragma, $info); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Pragmas/CastPragmaParser.php b/vendor/opis/json-schema/src/Parsers/Pragmas/CastPragmaParser.php new file mode 100644 index 000000000..e460fef82 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Pragmas/CastPragmaParser.php @@ -0,0 +1,45 @@ +pragmaExists($info)) { + return null; + } + + $value = $this->pragmaValue($info); + + if (!is_string($value) || !Helper::isValidJsonType($value)) { + throw $this->pragmaException('Pragma {pragma} must contain a valid json type name', $info); + } + + return new CastPragma($value); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Pragmas/GlobalsPragmaParser.php b/vendor/opis/json-schema/src/Parsers/Pragmas/GlobalsPragmaParser.php new file mode 100644 index 000000000..55dddb758 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Pragmas/GlobalsPragmaParser.php @@ -0,0 +1,48 @@ +option('allowGlobals') || !$this->pragmaExists($info)) { + return null; + } + + $value = $this->pragmaValue($info); + + if (!is_object($value)) { + throw $this->pragmaException('Pragma {pragma} must be an object', $info); + } + + $value = get_object_vars($value); + + return $value ? new GlobalsPragma($this->createVariables($parser, $value)) : null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Pragmas/MaxErrorsPragmaParser.php b/vendor/opis/json-schema/src/Parsers/Pragmas/MaxErrorsPragmaParser.php new file mode 100644 index 000000000..168e7d10c --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Pragmas/MaxErrorsPragmaParser.php @@ -0,0 +1,44 @@ +pragmaExists($info)) { + return null; + } + + $value = $this->pragmaValue($info); + + if (!is_int($value)) { + throw $this->pragmaException('Pragma {pragma} must be an integer', $info); + } + + return new MaxErrorsPragma($value); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Pragmas/SlotsPragmaParser.php b/vendor/opis/json-schema/src/Parsers/Pragmas/SlotsPragmaParser.php new file mode 100644 index 000000000..66f5edbf7 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Pragmas/SlotsPragmaParser.php @@ -0,0 +1,65 @@ +option('allowSlots') || !$this->pragmaExists($info)) { + return null; + } + + $value = $this->pragmaValue($info); + + if (!is_object($value)) { + throw $this->pragmaException('Pragma {pragma} must be an object', $info); + } + + $list = []; + + foreach ($value as $name => $slot) { + if ($slot === null) { + continue; + } + + if (is_bool($slot)) { + + $list[$name] = $parser->parseSchema(new SchemaInfo( + $slot, null, $info->base(), $info->root(), + array_merge($info->path(), [$this->pragma, $name]), + $info->draft() ?? $parser->defaultDraftVersion() + )); + } elseif (is_string($slot) || is_object($slot)) { + $list[$name] = $slot; + } else { + throw $this->pragmaException('Pragma {pragma} contains invalid value for slot ' . $name, $info); + } + } + + return $list ? new SlotsPragma($list) : null; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/ResolverTrait.php b/vendor/opis/json-schema/src/Parsers/ResolverTrait.php new file mode 100644 index 000000000..0c66340f7 --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/ResolverTrait.php @@ -0,0 +1,38 @@ + $super) { + if (!isset($list[$sub]) && isset($list[$super])) { + $list[$sub] = $list[$super]; + } + } + + return $list; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/SchemaParser.php b/vendor/opis/json-schema/src/Parsers/SchemaParser.php new file mode 100644 index 000000000..4eef1860e --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/SchemaParser.php @@ -0,0 +1,644 @@ + true, + 'allowFormats' => true, + 'allowMappers' => true, + 'allowTemplates' => true, + 'allowGlobals' => true, + 'allowDefaults' => true, + 'allowSlots' => true, + 'allowKeywordValidators' => true, + 'allowPragmas' => true, + + 'allowDataKeyword' => true, + 'allowKeywordsAlongsideRef' => false, + 'allowUnevaluated' => true, + 'allowRelativeJsonPointerInRef' => true, + 'allowExclusiveMinMaxAsBool' => true, + + 'keepDependenciesKeyword' => true, + 'keepAdditionalItemsKeyword' => true, + + 'decodeContent' => ['06', '07'], + 'defaultDraft' => self::DEFAULT_DRAFT, + + 'varRefKey' => '$ref', + 'varEachKey' => '$each', + 'varDefaultKey' => 'default', + ]; + + /** @var array */ + protected array $options; + + /** @var Draft[] */ + protected array $drafts; + + /** @var array */ + protected array $resolvers; + + /** + * @param array $resolvers + * @param array $options + * @param Vocabulary|null $extraVocabulary + */ + public function __construct( + array $resolvers = [], + array $options = [], + ?Vocabulary $extraVocabulary = null + ) + { + if ($options) { + $this->options = $options + self::DEFAULT_OPTIONS; + } else { + $this->options = self::DEFAULT_OPTIONS; + } + + $this->resolvers = $this->getResolvers($resolvers); + + $this->drafts = $this->getDrafts($extraVocabulary ?? new DefaultVocabulary()); + } + + /** + * @param Vocabulary|null $extraVocabulary + * @return array + */ + protected function getDrafts(?Vocabulary $extraVocabulary): array + { + return [ + '06' => new Draft06($extraVocabulary), + '07' => new Draft07($extraVocabulary), + '2019-09' => new Draft201909($extraVocabulary), + '2020-12' => new Draft202012($extraVocabulary), + ]; + } + + /** + * @param array $resolvers + * @return array + */ + protected function getResolvers(array $resolvers): array + { + if (!array_key_exists('format', $resolvers)) { + $resolvers['format'] = new FormatResolver(); + } + + if (!array_key_exists('contentEncoding', $resolvers)) { + $resolvers['contentEncoding'] = new ContentEncodingResolver(); + } + + if (!array_key_exists('contentMediaType', $resolvers)) { + $resolvers['contentMediaType'] = new ContentMediaTypeResolver(); + } + + if (!array_key_exists('$filters', $resolvers)) { + $resolvers['$filters'] = new FilterResolver(); + } + + return $resolvers; + } + + /** + * @param string $name + * @param null $default + * @return mixed|null + */ + public function option(string $name, $default = null) + { + return $this->options[$name] ?? $default; + } + + /** + * @param string $name + * @param $value + * @return $this + */ + public function setOption(string $name, $value): self + { + $this->options[$name] = $value; + + return $this; + } + + /** + * @return array + */ + public function getOptions(): array + { + return $this->options; + } + + /** + * @param string $name + * @param $resolver + * @return $this + */ + public function setResolver(string $name, $resolver): self + { + $this->resolvers[$name] = $resolver; + + return $this; + } + + /** + * @return null|FilterResolver + */ + public function getFilterResolver(): ?FilterResolver + { + return $this->getResolver('$filters'); + } + + /** + * @param null|FilterResolver $resolver + * @return $this + */ + public function setFilterResolver(?FilterResolver $resolver): self + { + return $this->setResolver('$filters', $resolver); + } + + /** + * @return null|FormatResolver + */ + public function getFormatResolver(): ?FormatResolver + { + return $this->getResolver('format'); + } + + /** + * @param FormatResolver|null $resolver + * @return $this + */ + public function setFormatResolver(?FormatResolver $resolver): self + { + return $this->setResolver('format', $resolver); + } + + /** + * @return null|ContentEncodingResolver + */ + public function getContentEncodingResolver(): ?ContentEncodingResolver + { + return $this->getResolver('contentEncoding'); + } + + /** + * @param ContentEncodingResolver|null $resolver + * @return $this + */ + public function setContentEncodingResolver(?ContentEncodingResolver $resolver): self + { + return $this->setResolver('contentEncoding', $resolver); + } + + /** + * @return null|ContentMediaTypeResolver + */ + public function getMediaTypeResolver(): ?ContentMediaTypeResolver + { + return $this->getResolver('contentMediaType'); + } + + /** + * @param ContentMediaTypeResolver|null $resolver + * @return $this + */ + public function setMediaTypeResolver(?ContentMediaTypeResolver $resolver): self + { + return $this->setResolver('contentMediaType', $resolver); + } + + /** + * @return string + */ + public function defaultDraftVersion(): string + { + return $this->option('defaultDraft', self::DEFAULT_DRAFT); + } + + /** + * @param string $draft + * @return $this + */ + public function setDefaultDraftVersion(string $draft): self + { + return $this->setOption('defaultDraft', $draft); + } + + /** + * @param string $schema + * @return string|null + */ + public function parseDraftVersion(string $schema): ?string + { + if (!preg_match(self::DRAFT_REGEX, $schema, $m)) { + return null; + } + + return $m[1] ?? null; + } + + /** + * @param object $schema + * @return string|null + */ + public function parseId(object $schema): ?string + { + if (property_exists($schema, '$id') && is_string($schema->{'$id'})) { + return $schema->{'$id'}; + } + + return null; + } + + /** + * @param object $schema + * @param string $draft + * @return string|null + */ + public function parseAnchor(object $schema, string $draft): ?string + { + if (!property_exists($schema, '$anchor') || + !isset($this->drafts[$draft]) || + !$this->drafts[$draft]->supportsAnchorId()) { + return null; + } + + $anchor = $schema->{'$anchor'}; + + if (!is_string($anchor) || !preg_match(self::ANCHOR_REGEX, $anchor)) { + return null; + } + + return $anchor; + } + + /** + * @param object $schema + * @return string|null + */ + public function parseSchemaDraft(object $schema): ?string + { + if (!property_exists($schema, '$schema') || !is_string($schema->{'$schema'})) { + return null; + } + + return $this->parseDraftVersion($schema->{'$schema'}); + } + + /** + * @param object $schema + * @param Uri $id + * @param callable $handle_id + * @param callable $handle_object + * @param string|null $draft + * @return Schema|null + */ + public function parseRootSchema( + object $schema, + Uri $id, + callable $handle_id, + callable $handle_object, + ?string $draft = null + ): ?Schema + { + $existent = false; + if (property_exists($schema, '$id')) { + $existent = true; + $id = Uri::parse($schema->{'$id'}, true); + } + + if ($id instanceof Uri) { + if ($id->fragment() === null) { + $id = Uri::merge($id, null, true); + } + } else { + throw new ParseException('Root schema id must be an URI', new SchemaInfo($schema, $id)); + } + + if (!$id->isAbsolute()) { + throw new ParseException('Root schema id must be an absolute URI', new SchemaInfo($schema, $id)); + } + + if ($id->fragment() !== '') { + throw new ParseException('Root schema id must have an empty fragment or none', new SchemaInfo($schema, $id)); + } + + // Check if id exists + if ($resolved = $handle_id($id)) { + return $resolved; + } + + if (property_exists($schema, '$schema')) { + if (!is_string($schema->{'$schema'})) { + throw new ParseException('Schema draft must be a string', new SchemaInfo($schema, $id)); + } + $draft = $this->parseDraftVersion($schema->{'$schema'}); + } + + if ($draft === null) { + $draft = $this->defaultDraftVersion(); + } + + if (!$existent) { + $schema->{'$id'} = (string)$id; + } + + $resolved = $handle_object($schema, $id, $draft); + + if (!$existent) { + unset($schema->{'$id'}); + } + + return $resolved; + } + + /** + * @param SchemaInfo $info + * @return Schema + */ + public function parseSchema(SchemaInfo $info): Schema + { + if ($info->isBoolean()) { + return new BooleanSchema($info); + } + + try { + return $this->parseSchemaObject($info); + } catch (SchemaException $exception) { + return new ExceptionSchema($info, $exception); + } + } + + /** + * @param string $version + * @return Draft|null + */ + public function draft(string $version): ?Draft + { + return $this->drafts[$version] ?? null; + } + + /** + * @param Draft $draft + * @return $this + */ + public function addDraft(Draft $draft): self + { + $this->drafts[$draft->version()] = $draft; + + return $this; + } + + /** + * @return string[] + */ + public function supportedDrafts(): array + { + return array_keys($this->drafts); + } + + /** + * @param array $options + * @return $this + */ + protected function setOptions(array $options): self + { + $this->options = $options + $this->options; + + return $this; + } + + /** + * @param string $name + * @return mixed|null + */ + protected function getResolver(string $name) + { + $resolver = $this->resolvers[$name] ?? null; + + if (!is_object($resolver)) { + return null; + } + + return $resolver; + } + + /** + * @param SchemaInfo $info + * @return Schema + */ + protected function parseSchemaObject(SchemaInfo $info): Schema + { + $draftObject = $this->draft($info->draft()); + + if ($draftObject === null) { + throw new ParseException("Unsupported draft-{$info->draft()}", $info); + } + + /** @var object $schema */ + $schema = $info->data(); + + // Check id + if (property_exists($schema, '$id')) { + $id = $info->id(); + if ($id === null || !$id->isAbsolute()) { + throw new ParseException('Schema id must be a valid URI', $info); + } + } + + if ($hasRef = property_exists($schema, '$ref')) { + if ($this->option('allowKeywordsAlongsideRef') || $draftObject->allowKeywordsAlongsideRef()) { + $hasRef = false; + } + } + + $shared = (object) []; + + if ($this->option('allowKeywordValidators')) { + $keywordValidator = $this->parseKeywordValidators($info, $draftObject->keywordValidators(), $shared); + } else { + $keywordValidator = null; + } + + return $this->parseSchemaKeywords($info, $keywordValidator, $draftObject->keywords(), $shared, $hasRef); + } + + /** + * @param SchemaInfo $info + * @param KeywordValidatorParser[] $keywordValidators + * @param object $shared + * @return KeywordValidator|null + */ + protected function parseKeywordValidators(SchemaInfo $info, array $keywordValidators, object $shared): ?KeywordValidator + { + $last = null; + + while ($keywordValidators) { + /** @var KeywordValidatorParser $keywordValidator */ + $keywordValidator = array_pop($keywordValidators); + if ($keywordValidator && ($keyword = $keywordValidator->parse($info, $this, $shared))) { + $keyword->setNext($last); + $last = $keyword; + unset($keyword); + } + unset($keywordValidator); + } + + return $last; + } + + /** + * @param SchemaInfo $info + * @param KeywordValidator|null $keywordValidator + * @param KeywordParser[] $parsers + * @param object $shared + * @param bool $hasRef + * @return Schema + */ + protected function parseSchemaKeywords(SchemaInfo $info, ?KeywordValidator $keywordValidator, + array $parsers, object $shared, bool $hasRef = false): Schema + { + /** @var Keyword[] $prepend */ + $prepend = []; + /** @var Keyword[] $append */ + $append = []; + /** @var Keyword[] $before */ + $before = []; + /** @var Keyword[] $after */ + $after = []; + /** @var Keyword[][] $types */ + $types = []; + /** @var Keyword[] $ref */ + $ref = []; + + if ($hasRef) { + foreach ($parsers as $parser) { + $kType = $parser->type(); + + if ($kType === KeywordParser::TYPE_APPEND) { + $container = &$append; + } elseif ($kType === KeywordParser::TYPE_AFTER_REF) { + $container = &$ref; + } elseif ($kType === KeywordParser::TYPE_PREPEND) { + $container = &$prepend; + } else { + continue; + } + + if ($keyword = $parser->parse($info, $this, $shared)) { + $container[] = $keyword; + } + + unset($container, $keyword, $kType); + } + } else { + foreach ($parsers as $parser) { + $keyword = $parser->parse($info, $this, $shared); + if ($keyword === null) { + continue; + } + + $kType = $parser->type(); + + switch ($kType) { + case KeywordParser::TYPE_PREPEND: + $prepend[] = $keyword; + break; + case KeywordParser::TYPE_APPEND: + $append[] = $keyword; + break; + case KeywordParser::TYPE_BEFORE: + $before[] = $keyword; + break; + case KeywordParser::TYPE_AFTER: + $after[] = $keyword; + break; + case KeywordParser::TYPE_AFTER_REF: + $ref[] = $keyword; + break; + default: + if (!isset($types[$kType])) { + $types[$kType] = []; + } + $types[$kType][] = $keyword; + break; + + } + } + } + + unset($shared); + + if ($prepend) { + $before = array_merge($prepend, $before); + } + unset($prepend); + + if ($ref) { + $after = array_merge($after, $ref); + } + unset($ref); + + if ($append) { + $after = array_merge($after, $append); + } + unset($append); + + if (empty($before)) { + $before = null; + } + if (empty($after)) { + $after = null; + } + if (empty($types)) { + $types = null; + } + + if (empty($types) && empty($before) && empty($after)) { + return new EmptySchema($info, $keywordValidator); + } + + return new ObjectSchema($info, $keywordValidator, $types, $before, $after); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/VariablesTrait.php b/vendor/opis/json-schema/src/Parsers/VariablesTrait.php new file mode 100644 index 000000000..6022fb99d --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/VariablesTrait.php @@ -0,0 +1,41 @@ +option('varRefKey', '$ref'), + $parser->option('varEachKey', '$each'), + $parser->option('varDefaultKey', 'default') + ); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Parsers/Vocabulary.php b/vendor/opis/json-schema/src/Parsers/Vocabulary.php new file mode 100644 index 000000000..0f83fe23d --- /dev/null +++ b/vendor/opis/json-schema/src/Parsers/Vocabulary.php @@ -0,0 +1,126 @@ +keywords = $keywords; + $this->keywordValidators = $keywordValidators; + $this->pragmas = $pragmas; + } + + /** + * @return KeywordParser[] + */ + public function keywords(): array + { + return $this->keywords; + } + + /** + * @return KeywordValidatorParser[] + */ + public function keywordValidators(): array + { + return $this->keywordValidators; + } + + /** + * @return PragmaParser[] + */ + public function pragmas(): array + { + return $this->pragmas; + } + + /** + * @param KeywordParser $keyword + * @return Vocabulary + */ + public function appendKeyword(KeywordParser $keyword): self + { + $this->keywords[] = $keyword; + return $this; + } + + /** + * @param KeywordParser $keyword + * @return Vocabulary + */ + public function prependKeyword(KeywordParser $keyword): self + { + array_unshift($this->keywords, $keyword); + return $this; + } + + /** + * @param KeywordValidatorParser $keywordValidatorParser + * @return Vocabulary + */ + public function appendKeywordValidator(KeywordValidatorParser $keywordValidatorParser): self + { + $this->keywordValidators[] = $keywordValidatorParser; + return $this; + } + + /** + * @param KeywordValidatorParser $keywordValidator + * @return Vocabulary + */ + public function prependKeywordValidator(KeywordValidatorParser $keywordValidator): self + { + array_unshift($this->keywordValidators, $keywordValidator); + return $this; + } + + /** + * @param PragmaParser $pragma + * @return Vocabulary + */ + public function appendPragma(PragmaParser $pragma): self + { + $this->pragmas[] = $pragma; + return $this; + } + + /** + * @param PragmaParser $pragma + * @return Vocabulary + */ + public function prependPragma(PragmaParser $pragma): self + { + array_unshift($this->pragmas, $pragma); + return $this; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Pragma.php b/vendor/opis/json-schema/src/Pragma.php new file mode 100644 index 000000000..fc8860b4d --- /dev/null +++ b/vendor/opis/json-schema/src/Pragma.php @@ -0,0 +1,33 @@ +cast = $cast; + $this->func = $this->getCastFunction($cast); + } + + /** + * @inheritDoc + */ + public function enter(ValidationContext $context) + { + $currentType = $context->currentDataType(); + if ($currentType !== null && Helper::jsonTypeMatches($currentType, $this->cast)) { + // Cast not needed + return $this; + } + unset($currentType); + + $currentData = $context->currentData(); + + $context->setCurrentData(($this->func)($currentData)); + + return $currentData; + } + + /** + * @inheritDoc + */ + public function leave(ValidationContext $context, $data): void + { + if ($data !== $this) { + $context->setCurrentData($data); + } + } + + /** + * @param string $type + * @return callable + */ + protected function getCastFunction(string $type): callable + { + $f = 'toNull'; + + switch ($type) { + case 'integer': + $f = 'toInteger'; + break; + case 'number': + $f = 'toNumber'; + break; + case 'string': + $f = 'toString'; + break; + case 'array': + $f = 'toArray'; + break; + case 'object': + $f = 'toObject'; + break; + case 'boolean': + $f = 'toBoolean'; + break; + } + + return [$this, $f]; + } + + /** + * @param $value + * @return int|null + */ + public function toInteger($value): ?int + { + if ($value === null) { + return 0; + } + + return is_scalar($value) ? intval($value) : null; + } + + /** + * @param $value + * @return float|null + */ + public function toNumber($value): ?float + { + if ($value === null) { + return 0.0; + } + + return is_scalar($value) ? floatval($value) : null; + } + + /** + * @param $value + * @return string|null + */ + public function toString($value): ?string + { + if ($value === null) { + return ''; + } + + if (is_scalar($value)) { + return (string) $value; + } + + return null; + } + + /** + * @param $value + * @return array|null + */ + public function toArray($value): ?array + { + if ($value === null) { + return []; + } + + if (is_scalar($value)) { + return [$value]; + } + + if (is_array($value)) { + return array_values($value); + } + + if (is_object($value)) { + return array_values(get_object_vars($value)); + } + + return null; + } + + /** + * @param $value + * @return object|null + */ + public function toObject($value): ?object + { + if (is_object($value) || is_array($value)) { + return (object) $value; + } + + return null; + } + + /** + * @param $value + * @return bool + */ + public function toBoolean($value): bool + { + if ($value === null) { + return false; + } + if (is_string($value)) { + return !($value === ''); + } + if (is_object($value)) { + return count(get_object_vars($value)) > 0; + } + return boolval($value); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Pragmas/GlobalsPragma.php b/vendor/opis/json-schema/src/Pragmas/GlobalsPragma.php new file mode 100644 index 000000000..e1233cf1e --- /dev/null +++ b/vendor/opis/json-schema/src/Pragmas/GlobalsPragma.php @@ -0,0 +1,61 @@ +globals = $globals; + } + + /** + * @inheritDoc + */ + public function enter(ValidationContext $context) + { + $resolved = (array) $this->globals->resolve($context->rootData(), $context->currentDataPath()); + if (!$resolved) { + return null; + } + + $data = $context->globals(); + $context->setGlobals($resolved, false); + return $data; + } + + /** + * @inheritDoc + */ + public function leave(ValidationContext $context, $data): void + { + if ($data === null) { + return; + } + $context->setGlobals($data, true); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Pragmas/MaxErrorsPragma.php b/vendor/opis/json-schema/src/Pragmas/MaxErrorsPragma.php new file mode 100644 index 000000000..0d1462645 --- /dev/null +++ b/vendor/opis/json-schema/src/Pragmas/MaxErrorsPragma.php @@ -0,0 +1,55 @@ +maxErrors = $maxErrors; + } + + /** + * @inheritDoc + */ + public function enter(ValidationContext $context) + { + $data = $context->maxErrors(); + $context->setMaxErrors($this->maxErrors); + return $data; + } + + /** + * @inheritDoc + */ + public function leave(ValidationContext $context, $data): void + { + if ($data === null) { + return; + } + $context->setMaxErrors($data); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Pragmas/SlotsPragma.php b/vendor/opis/json-schema/src/Pragmas/SlotsPragma.php new file mode 100644 index 000000000..7806a75cd --- /dev/null +++ b/vendor/opis/json-schema/src/Pragmas/SlotsPragma.php @@ -0,0 +1,52 @@ +slots = $slots; + } + + /** + * @inheritDoc + */ + public function enter(ValidationContext $context) + { + $data = $context->slots(); + $context->setSlots($data ? $this->slots + $data : $this->slots); + return $data; + } + + /** + * @inheritDoc + */ + public function leave(ValidationContext $context, $data): void + { + $context->setSlots($data); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Resolvers/ContentEncodingResolver.php b/vendor/opis/json-schema/src/Resolvers/ContentEncodingResolver.php new file mode 100644 index 000000000..8c49b83df --- /dev/null +++ b/vendor/opis/json-schema/src/Resolvers/ContentEncodingResolver.php @@ -0,0 +1,134 @@ + self::class . '::DecodeBinary', + 'base64' => self::class . '::DecodeBase64', + 'quoted-printable' => self::class . '::DecodeQuotedPrintable', + ]; + + $this->list = $list; + $this->defaultEncoding = $defaultEncoding; + } + + /** + * @param string $name + * @return callable|ContentEncoding|string|null + */ + public function resolve(string $name) + { + return $this->list[$name] ?? $this->defaultEncoding; + } + + /** + * @param string $name + * @param ContentEncoding $encoding + * @return ContentEncodingResolver + */ + public function register(string $name, ContentEncoding $encoding): self + { + $this->list[$name] = $encoding; + + return $this; + } + + /** + * @param string $name + * @param callable $encoding + * @return ContentEncodingResolver + */ + public function registerCallable(string $name, callable $encoding): self + { + $this->list[$name] = $encoding; + + return $this; + } + + /** + * @param string $name + * @return bool + */ + public function unregister(string $name): bool + { + if (isset($this->list[$name])) { + unset($this->list[$name]); + + return true; + } + + return false; + } + + /** + * @param callable|ContentEncoding|null $handler + * @return $this + */ + public function setDefaultHandler($handler): self + { + $this->defaultEncoding = $handler; + return $this; + } + + public function __serialize(): array + { + return [ + 'list' => $this->list, + 'defaultEncoding' => $this->defaultEncoding, + ]; + } + + public function __unserialize(array $data): void + { + $this->list = $data['list']; + $this->defaultEncoding = $data['defaultEncoding'] ?? null; + } + + public static function DecodeBinary(string $value): ?string + { + return $value; + } + + public static function DecodeBase64(string $value): ?string + { + $value = base64_decode($value, true); + + return is_string($value) ? $value : null; + } + + public static function DecodeQuotedPrintable(string $value): ?string + { + return quoted_printable_decode($value); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Resolvers/ContentMediaTypeResolver.php b/vendor/opis/json-schema/src/Resolvers/ContentMediaTypeResolver.php new file mode 100644 index 000000000..2b95799ff --- /dev/null +++ b/vendor/opis/json-schema/src/Resolvers/ContentMediaTypeResolver.php @@ -0,0 +1,145 @@ + self::class . '::IsJsonEncoded', + ]; + + $this->media = $media; + $this->defaultMedia = $defaultMedia ?? self::class . '::IsEncodedAsType'; + } + + /** + * @param string $name + * @return callable|ContentMediaType|string|null + */ + public function resolve(string $name) + { + return $this->media[$name] ?? $this->defaultMedia; + } + + /** + * @param string $name + * @param ContentMediaType $media + * @return ContentMediaTypeResolver + */ + public function register(string $name, ContentMediaType $media): self + { + $this->media[$name] = $media; + + return $this; + } + + /** + * @param string $name + * @param callable $media + * @return ContentMediaTypeResolver + */ + public function registerCallable(string $name, callable $media): self + { + $this->media[$name] = $media; + + return $this; + } + + /** + * @param string $name + * @return bool + */ + public function unregister(string $name): bool + { + if (isset($this->media[$name])) { + unset($this->media[$name]); + + return true; + } + + return false; + } + + /** + * @param callable|ContentMediaType|null $handler + * @return ContentMediaTypeResolver + */ + public function setDefaultHandler($handler): self + { + $this->defaultMedia = $handler; + + return $this; + } + + public function __serialize(): array + { + return [ + 'media' => $this->media, + 'defaultMedia' => $this->defaultMedia, + ]; + } + + public function __unserialize(array $data): void + { + $this->media = $data['media']; + $this->defaultMedia = $data['defaultMedia']; + } + + public static function IsJsonEncoded(string $value, + /** @noinspection PhpUnusedParameterInspection */ string $type): bool + { + json_decode($value); + + return json_last_error() === JSON_ERROR_NONE; + } + + public static function IsEncodedAsType(string $value, string $type): bool + { + /** @var finfo|null|bool $finfo */ + static $finfo = false; + + if ($finfo === false) { + if (!class_exists(finfo::class)) { + $finfo = null; + return false; + } + $finfo = new finfo(FILEINFO_MIME_TYPE); + } elseif (!$finfo) { + return false; + } + + $r = $finfo->buffer($value); + + return $r == $type || $r == 'application/x-empty'; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Resolvers/FilterResolver.php b/vendor/opis/json-schema/src/Resolvers/FilterResolver.php new file mode 100644 index 000000000..d3687ef56 --- /dev/null +++ b/vendor/opis/json-schema/src/Resolvers/FilterResolver.php @@ -0,0 +1,265 @@ +separator = $ns_separator; + $this->defaultNS = $default_ns; + + $this->registerDefaultFilters(); + } + + /** + * You can override this to add/remove default filters + */ + protected function registerDefaultFilters(): void + { + $this->registerMultipleTypes("schema-exists", new SchemaExistsFilter()); + $this->registerMultipleTypes("data-exists", new DataExistsFilter()); + $this->registerMultipleTypes("global-exists", new GlobalVarExistsFilter()); + $this->registerMultipleTypes("slot-exists", new SlotExistsFilter()); + $this->registerMultipleTypes("filter-exists", new FilterExistsFilter()); + $this->registerMultipleTypes("format-exists", new FormatExistsFilter()); + + $cls = DateTimeFilters::class . "::"; + $this->registerCallable("string", "min-date", $cls . "MinDate"); + $this->registerCallable("string", "max-date", $cls . "MaxDate"); + $this->registerCallable("string", "not-date", $cls . "NotDate"); + $this->registerCallable("string", "min-time", $cls . "MinTime"); + $this->registerCallable("string", "max-time", $cls . "MaxTime"); + $this->registerCallable("string", "min-datetime", $cls . "MinDateTime"); + $this->registerCallable("string", "max-datetime", $cls . "MaxDateTime"); + + $cls = CommonFilters::class . "::"; + $this->registerCallable("string", "regex", $cls . "Regex"); + $this->registerMultipleTypes("equals", $cls . "Equals"); + } + + + /** + * @param string $name + * @param string $type + * @return Filter|callable|null + */ + public function resolve(string $name, string $type) + { + [$ns, $name] = $this->parseName($name); + + if (isset($this->filters[$ns][$name])) { + return $this->filters[$ns][$name][$type] ?? null; + } + + if (!isset($this->ns[$ns])) { + return null; + } + + $this->filters[$ns][$name] = $this->ns[$ns]->resolveAll($name); + + return $this->filters[$ns][$name][$type] ?? null; + } + + /** + * @param string $name + * @return Filter[]|callable[]|null + */ + public function resolveAll(string $name): ?array + { + [$ns, $name] = $this->parseName($name); + + if (isset($this->filters[$ns][$name])) { + return $this->filters[$ns][$name]; + } + + if (!isset($this->ns[$ns])) { + return null; + } + + return $this->filters[$ns][$name] = $this->ns[$ns]->resolveAll($name); + } + + /** + * @param string $type + * @param string $name + * @param Filter $filter + * @return FilterResolver + */ + public function register(string $type, string $name, Filter $filter): self + { + [$ns, $name] = $this->parseName($name); + + $this->filters[$ns][$name][$type] = $filter; + + return $this; + } + + /** + * @param string $name + * @param string|null $type + * @return bool + */ + public function unregister(string $name, ?string $type = null): bool + { + [$ns, $name] = $this->parseName($name); + if (!isset($this->filters[$ns][$name])) { + return false; + } + + if ($type === null) { + unset($this->filters[$ns][$name]); + + return true; + } + + if (isset($this->filters[$ns][$name][$type])) { + unset($this->filters[$ns][$name][$type]); + + return true; + } + + return false; + } + + /** + * @param string $name + * @param callable|Filter $filter + * @param array|null $types + * @return FilterResolver + */ + public function registerMultipleTypes(string $name, $filter, ?array $types = null): self + { + [$ns, $name] = $this->parseName($name); + + $types = $types ?? Helper::JSON_TYPES; + + foreach ($types as $type) { + $this->filters[$ns][$name][$type] = $filter; + } + + return $this; + } + + /** + * @param string $type + * @param string $name + * @param callable $filter + * @return FilterResolver + */ + public function registerCallable(string $type, string $name, callable $filter): self + { + [$ns, $name] = $this->parseName($name); + + $this->filters[$ns][$name][$type] = $filter; + + return $this; + } + + /** + * @param string $ns + * @param FilterResolver $resolver + * @return FilterResolver + */ + public function registerNS(string $ns, FilterResolver $resolver): self + { + $this->ns[$ns] = $resolver; + + return $this; + } + + /** + * @param string $ns + * @return bool + */ + public function unregisterNS(string $ns): bool + { + if (isset($this->filters[$ns])) { + unset($this->filters[$ns]); + unset($this->ns[$ns]); + + return true; + } + + if (isset($this->ns[$ns])) { + unset($this->ns[$ns]); + + return true; + } + + return false; + } + + public function __serialize(): array + { + return [ + 'separator' => $this->separator, + 'defaultNS' => $this->defaultNS, + 'ns' => $this->ns, + 'filters' => $this->filters, + ]; + } + + public function __unserialize(array $data): void + { + $this->separator = $data['separator']; + $this->defaultNS = $data['defaultNS']; + $this->ns = $data['ns']; + $this->filters = $data['filters']; + } + + /** + * @param string $name + * @return array + */ + protected function parseName(string $name): array + { + $name = strtolower($name); + + if (strpos($name, $this->separator) === false) { + return [$this->defaultNS, $name]; + } + + return explode($this->separator, $name, 2); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Resolvers/FormatResolver.php b/vendor/opis/json-schema/src/Resolvers/FormatResolver.php new file mode 100644 index 000000000..61a3fdc56 --- /dev/null +++ b/vendor/opis/json-schema/src/Resolvers/FormatResolver.php @@ -0,0 +1,143 @@ +formats = [ + 'string' => [ + 'date' => DateTimeFormats::class . '::date', + 'time' => DateTimeFormats::class . '::time', + 'date-time' => DateTimeFormats::class . '::dateTime', + 'duration' => DateTimeFormats::class . '::duration', + + 'uri' => UriFormats::class . '::uri', + 'uri-reference' => UriFormats::class . '::uriReference', + 'uri-template' => UriFormats::class . '::uriTemplate', + + 'regex' => Helper::class . '::isValidPattern', + 'ipv4' => MiscFormats::class . '::ipv4', + 'ipv6' => MiscFormats::class . '::ipv6', + 'uuid' => MiscFormats::class . '::uuid', + + 'email' => MiscFormats::class . '::email', + 'hostname' => Uri::class . '::isValidHost', + + 'json-pointer' => JsonPointer::class . '::isAbsolutePointer', + 'relative-json-pointer' => JsonPointer::class . '::isRelativePointer', + + 'idn-hostname' => IriFormats::class . '::idnHostname', + 'idn-email' => IriFormats::class . '::idnEmail', + 'iri' => IriFormats::class . '::iri', + 'iri-reference' => IriFormats::class . '::iriReference', + ], + ]; + } + + /** + * @param string $name + * @param string $type + * @return callable|Format|null + */ + public function resolve(string $name, string $type) + { + return $this->formats[$type][$name] ?? null; + } + + /** + * @param string $name + * @return Format[]|callable[]|null + */ + public function resolveAll(string $name): ?array + { + $list = null; + + foreach ($this->formats as $type => $items) { + if (isset($items[$name])) { + $list[$type] = $items[$name]; + } + } + + return $list; + } + + /** + * @param string $type + * @param string $name + * @param Format $format + * @return FormatResolver + */ + public function register(string $type, string $name, Format $format): self + { + $this->formats[$type][$name] = $format; + + return $this; + } + + /** + * @param string $type + * @param string $name + * @param callable $format + * @return FormatResolver + */ + public function registerCallable(string $type, string $name, callable $format): self + { + $this->formats[$type][$name] = $format; + + return $this; + } + + /** + * @param string $type + * @param string $name + * @return bool + */ + public function unregister(string $type, string $name): bool + { + if (isset($this->formats[$type][$name])) { + unset($this->formats[$type][$name]); + + return true; + } + + return false; + } + + public function __serialize(): array + { + return ['formats' => $this->formats]; + } + + public function __unserialize(array $data): void + { + $this->formats = $data['formats']; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Resolvers/SchemaResolver.php b/vendor/opis/json-schema/src/Resolvers/SchemaResolver.php new file mode 100644 index 000000000..93c7d0a53 --- /dev/null +++ b/vendor/opis/json-schema/src/Resolvers/SchemaResolver.php @@ -0,0 +1,342 @@ +isAbsolute()) { + return null; + } + + $scheme = $uri->scheme(); + if (isset($this->protocols[$scheme])) { + return ($this->protocols[$scheme])($uri); + } + + $id = (string) $uri; + if (isset($this->raw[$id])) { + return $this->raw[$id]; + } + + $path = $this->resolvePath($uri); + + if ($path === null || !is_file($path)) { + return null; + } + + $data = file_get_contents($path); + if (!is_string($data)) { + return null; + } + + $data = json_decode($data, false); + + return $data; + } + + /** + * @param bool|object|string $schema + * @param string|null $id + * @return bool + */ + public function registerRaw($schema, ?string $id = null): bool + { + if (is_string($schema)) { + $schema = json_decode($schema, false); + } + + if ($id !== null && strpos($id, '#') === false) { + $id .= '#'; + } + + if (is_bool($schema)) { + if ($id === null) { + return false; + } + $this->raw[$id] = $schema; + return true; + } + + if (!is_object($schema)) { + return false; + } + + + if ($id === null) { + if (!isset($schema->{'$id'}) || !is_string($schema->{'$id'})) { + return false; + } + + $id = $schema->{'$id'}; + if (strpos($id, '#') === false) { + $id .= '#'; + } + } + + $this->raw[$id] = $schema; + + return true; + } + + /** + * @param string $id + * @return bool + */ + public function unregisterRaw(string $id): bool + { + if (strpos($id, '#') === false) { + $id .= '#'; + } + + if (isset($this->raw[$id])) { + unset($this->raw[$id]); + return true; + } + + return false; + } + + /** + * @param string $id + * @param string $file + * @return SchemaResolver + */ + public function registerFile(string $id, string $file): self + { + if (strpos($id, '#') === false) { + $id .= '#'; + } + + $this->files[$id] = $file; + + return $this; + } + + /** + * @param string $id + * @return bool + */ + public function unregisterFile(string $id): bool + { + if (strpos($id, '#') === false) { + $id .= '#'; + } + + if (!isset($this->files[$id])) { + return false; + } + + unset($this->files[$id]); + + return true; + } + + /** + * @param string $scheme + * @param callable $handler + * @return SchemaResolver + */ + public function registerProtocol(string $scheme, callable $handler): self + { + $this->protocols[$scheme] = $handler; + + return $this; + } + + /** + * @param string $scheme + * @return bool + */ + public function unregisterProtocol(string $scheme): bool + { + if (isset($this->protocols[$scheme])) { + unset($this->protocols[$scheme]); + + return true; + } + + return false; + } + + /** + * @param string $scheme + * @param string $host + * @param string|null $dir + * @return SchemaResolver + */ + public function registerProtocolDir(string $scheme, string $host, ?string $dir): self + { + if ($dir === null) { + unset($this->dirs[$scheme][$host]); + } else { + $this->dirs[$scheme][$host] = rtrim($dir, '/'); + } + + return $this; + } + + /** + * @param string $scheme + * @return bool + */ + public function unregisterProtocolDirs(string $scheme): bool + { + if (isset($this->dirs[$scheme])) { + unset($this->dirs[$scheme]); + + return true; + } + + return false; + } + + /** + * @param string $prefix + * @param string $dir + * @return SchemaResolver + */ + public function registerPrefix(string $prefix, string $dir): self + { + $this->prefixes[$prefix] = rtrim($dir, '/'); + + // Sort + uksort($this->prefixes, [$this, 'sortPrefixKeys']); + + return $this; + } + + /** + * @param string $prefix + * @return SchemaResolver + */ + public function unregisterPrefix(string $prefix): self + { + if (isset($this->prefixes[$prefix])) { + unset($this->prefixes[$prefix]); + // Sort + uksort($this->prefixes, [$this, 'sortPrefixKeys']); + } + + return $this; + } + + + public function __serialize(): array + { + return [ + 'raw' => $this->raw, + 'protocols' => $this->protocols, + 'prefixes' => $this->prefixes, + 'dirs' => $this->dirs, + ]; + } + + public function __unserialize(array $data): void + { + $this->raw = $data['raw']; + $this->protocols = $data['protocols']; + $this->prefixes = $data['prefixes']; + $this->dirs = $data['dirs']; + } + + /** + * @param string $a + * @param string $b + * @return int + */ + protected function sortPrefixKeys(string $a, string $b): int + { + $la = strlen($a); + $lb = strlen($b); + + if ($lb > $la) { + return 1; + } + + if ($lb === $la) { + return $b < $a ? 1 : ($b === $a ? 0 : -1); + } + + return -1; + } + + /** + * @param Uri $uri + * @return string|null + */ + protected function resolvePath(Uri $uri): ?string + { + $id = (string)$uri; + + if (isset($this->files[$id])) { + return $this->files[$id]; + } + + $scheme = $uri->scheme(); + + if (isset($this->dirs[$scheme])) { + $host = (string)$uri->host(); + if (isset($this->dirs[$scheme][$host])) { + return $this->dirs[$scheme][$host] . '/' . ltrim($uri->path(), '/'); + } + unset($host); + } + + $path = null; + foreach ($this->prefixes as $prefix => $dir) { + if ($prefix === '' || strpos($id, $prefix) === 0) { + $path = substr($id, strlen($prefix)); + if ($path === false || $path === '') { + $path = null; + continue; + } + $path = Uri::parseComponents($path); + if ($path && isset($path['path'])) { + $path = $dir . '/' . ltrim($path['path'], '/'); + break; + } + $path = null; + } + } + + return $path; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Schema.php b/vendor/opis/json-schema/src/Schema.php new file mode 100644 index 000000000..821a6a021 --- /dev/null +++ b/vendor/opis/json-schema/src/Schema.php @@ -0,0 +1,28 @@ +dataCache = new SplObjectStorage(); + $this->parser = $parser ?? new SchemaParser(); + $this->resolver = $resolver; + $this->decodeJsonString = $decodeJsonString; + } + + public function baseUri(): ?Uri + { + return $this->base; + } + + public function setBaseUri(?Uri $uri): self + { + $this->base = $uri; + return $this; + } + + public function parser(): SchemaParser + { + return $this->parser; + } + + public function setParser(SchemaParser $parser): self + { + $this->parser = $parser; + + return $this; + } + + public function resolver(): ?SchemaResolver + { + return $this->resolver; + } + + public function setResolver(?SchemaResolver $resolver): self + { + $this->resolver = $resolver; + + return $this; + } + + /** + * @param object $data + * @param null $id + * @param string|null $draft + * @return Schema + */ + public function loadObjectSchema(object $data, $id = null, ?string $draft = null): Schema + { + // Check if already loaded + if ($schema = $this->checkExistingObject($data)) { + return $schema; + } + + if (!$id) { + $id = $this->createSchemaId($data); + } + + $handle_id = fn (Uri $id): ?Schema => $this->checkExistingUri($id); + + $handle_object = function (object $data, Uri $id, string $draft): ?Schema { + $this->handleObject($data, $id, null, null, [], $draft, (string)$id); + + return $this->checkExistingObject($data); + }; + + return $this->parser->parseRootSchema($data, Uri::parse($id, true), $handle_id, $handle_object, $draft); + } + + /** + * @param bool $data + * @param null $id + * @param string|null $draft + * @return Schema + */ + public function loadBooleanSchema(bool $data, $id = null, ?string $draft = null): Schema + { + if (!$id) { + $id = $this->createSchemaId($data); + } + + return $this->parser->parseSchema(new SchemaInfo($data, Uri::parse($id, true), null, null, [], $draft)); + } + + /** + * @param Uri $uri + * @return Schema|null + */ + public function loadSchemaById(Uri $uri): ?Schema + { + if (!$uri->isAbsolute()) { + if ($this->base === null || !$this->base->isAbsolute()) { + return null; + } + $uri = $this->base->resolveRef($uri); + } + + $fragment = $uri->fragment(); + if ($fragment === null) { + $uri = Uri::merge($uri, null, true); + $fragment = ''; + } + + $schema = $this->checkExistingUri($uri); + + if ($schema !== null) { + return $schema; + } + + if ($fragment === '') { + return $this->resolve($uri); + } + + $root = Uri::merge('#', $uri); + + // Check if already resolved + if (($schema = $this->checkExistingUri($root)) === null) { + // Try to resolve + if (($schema = $this->resolve($root)) === null) { + // Schema not found + return null; + } + } + + // Resolve json pointer + if ($fragment !== '' && $schema && $schema->info()->isObject() && + ($pointer = JsonPointer::parse($fragment)) && $pointer->isAbsolute()) { + $object = $pointer->data($schema->info()->data()); + if (is_bool($object)) { + $schema = $this->loadBooleanSchema($object, $uri, $schema->info()->draft()); + } elseif (is_object($object)) { + $schema = $this->loadObjectSchema($object, $uri, $schema->info()->draft()); + } else { + $schema = null; + } + if ($schema) { + $key = $this->cacheKey((string) $uri); + $this->uriCache[$key] = $schema; + return $schema; + } + } + + // Check fragment + return $this->checkExistingUri($uri); + } + + /** + * Clears internal cache + */ + public function clearCache(): void + { + $this->dataCache->removeAll($this->dataCache); + $this->uriCache = []; + } + + /** + * @param Uri $uri + * @return null|Schema + */ + protected function resolve(Uri $uri): ?Schema + { + if ($this->resolver === null) { + return null; + } + + $data = $this->resolver->resolve($uri); + + if ($this->decodeJsonString && is_string($data)) { + $data = json_decode($data, false); + } + + if (is_bool($data)) { + $this->handleBoolean($data, $uri, null, null, [], $this->parser->defaultDraftVersion(), (string)$uri); + + return $this->checkExistingUri($uri); + } + + if (is_object($data)) { + if ($data instanceof Schema) { + return $data; + } + + $this->handleObject($data, $uri, null, null, [], $this->parser->defaultDraftVersion(), (string)$uri); + + return $this->checkExistingObject($data); + } + + return null; + } + + /** + * @param object $data + * @return null|Schema + */ + protected function checkExistingObject(object $data): ?Schema + { + if (!$this->dataCache->contains($data)) { + return null; + } + + $schema = $this->dataCache[$data]; + + if ($schema instanceof LazySchema) { + $schema = $schema->schema(); + $this->dataCache[$data] = $schema; + } elseif (!($schema instanceof Schema)) { + $schema = null; + } + + return $schema; + } + + /** + * @param Uri $uri + * @return null|Schema + */ + protected function checkExistingUri(Uri $uri): ?Schema + { + if ($uri->fragment() === null || !$uri->isAbsolute()) { + return null; + } + + $key = $this->cacheKey((string)$uri); + + if (!isset($this->uriCache[$key])) { + return null; + } + + $schema = $this->uriCache[$key]; + + if (!($schema instanceof Schema)) { + return $this->uriCache[$key] = $this->checkExistingObject($schema); + } + + if ($schema instanceof LazySchema) { + $schema = $schema->schema(); + $this->uriCache[$key] = $schema; + } + + return $schema; + } + + /** + * @param bool $data + * @param Uri|null $id + * @param Uri|null $base + * @param Uri|null $root + * @param array $path + * @param string $draft + * @param string $pointer + */ + protected function handleBoolean( + bool $data, + ?Uri $id, + ?Uri $base, + ?Uri $root, + array $path, + string $draft, + string $pointer + ) + { + $key = $this->cacheKey($pointer); + if (isset($this->uriCache[$key])) { + return; + } + + $this->uriCache[$key] = $this->parser->parseSchema(new SchemaInfo($data, $id, $base, $root, $path, $draft)); + } + + /** + * @param array $data + * @param Uri $base + * @param Uri $root + * @param array $path + * @param string $draft + * @param string $pointer + */ + protected function handleArray(array $data, Uri $base, Uri $root, array $path, string $draft, string $pointer) + { + foreach ($data as $key => $value) { + if (!is_int($key)) { + continue; + } + + if (is_bool($value)) { + $this->handleBoolean($value, null, $base, $root, array_merge($path, [$key]), $draft, + $pointer . '/' . $key); + } elseif (is_array($value)) { + $this->handleArray($value, $base, $root, array_merge($path, [$key]), $draft, $pointer . '/' . $key); + } elseif (is_object($value)) { + $this->handleObject($value, null, $base, $root, array_merge($path, [$key]), $draft, + $pointer . '/' . $key); + } + } + } + + /** + * @param object $data + * @param Uri|null $id + * @param Uri|null $base + * @param Uri|null $root + * @param array $path + * @param string $draft + * @param string $pointer + */ + protected function handleObject( + object $data, + ?Uri $id, + ?Uri $base, + ?Uri $root, + array $path, + string $draft, + string $pointer + ) + { + $schema_id = $this->parser->parseId($data); + $schema_anchor = $this->parser->parseAnchor($data, $draft); + $draft = $this->parser->parseSchemaDraft($data) ?? $draft; + + if ($schema_id !== null) { + $id = Uri::merge($schema_id, $base, true); + } elseif ($schema_anchor !== null) { + $id = Uri::merge('#' . $schema_anchor, $base, true); + } + + $lazy = new LazySchema(new SchemaInfo($data, $id, $base, $root, $path, $draft), $this->parser); + + if ($id && $id->isAbsolute()) { + $key = $this->cacheKey((string)$id); + if (isset($this->uriCache[$key])) { + throw new DuplicateSchemaIdException($id, $data); + } + $this->uriCache[$key] = $lazy; + } + + // When $id and $anchor are both present add a reference to the same lazy object + if ($schema_id !== null && $schema_anchor !== null) { + $anchor_id = Uri::merge('#' . $schema_anchor, $id, true); + $key = $this->cacheKey((string)$anchor_id); + if (isset($this->uriCache[$key])) { + throw new DuplicateSchemaIdException($anchor_id, $data); + } + $this->uriCache[$key] = $lazy; + } + + $this->dataCache[$data] = $lazy; + $this->uriCache[$this->cacheKey($pointer)] = $lazy; + + if ($root === null) { + $root = $id; + } + + if ($base === null) { + $base = $id ?? $root; + } elseif ($id !== null) { + $base = $id; + } + + foreach ($data as $key => $value) { + if (!is_string($key)) { + continue; + } + if (is_bool($value)) { + $this->handleBoolean($value, null, $base, $root, array_merge($path, [$key]), $draft, + $pointer . '/' . JsonPointer::encodePath($key)); + } elseif (is_array($value)) { + $this->handleArray($value, $base, $root, array_merge($path, [$key]), $draft, + $pointer . '/' . JsonPointer::encodePath($key)); + } elseif (is_object($value)) { + $this->handleObject($value, null, $base, $root, array_merge($path, [$key]), $draft, + $pointer . '/' . JsonPointer::encodePath($key)); + } + } + } + + /** + * @param string $path + * @return string + */ + protected function cacheKey(string $path): string + { + return isset($path[32]) ? md5($path) : $path; + } + + /** + * @param bool|object $data + * @return string + */ + protected function createSchemaId($data): string + { + if (is_bool($data)) { + $data = $data ? 'true' : 'false'; + } else { + $data = spl_object_hash($data); + } + + return "schema:///{$data}.json"; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/SchemaValidator.php b/vendor/opis/json-schema/src/SchemaValidator.php new file mode 100644 index 000000000..a8b6e3ef8 --- /dev/null +++ b/vendor/opis/json-schema/src/SchemaValidator.php @@ -0,0 +1,29 @@ +info = $info; + } + + /** + * @inheritDoc + */ + public function info(): SchemaInfo + { + return $this->info; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Schemas/BooleanSchema.php b/vendor/opis/json-schema/src/Schemas/BooleanSchema.php new file mode 100644 index 000000000..2c3a20946 --- /dev/null +++ b/vendor/opis/json-schema/src/Schemas/BooleanSchema.php @@ -0,0 +1,49 @@ +data = $info->data(); + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context): ?ValidationError + { + if ($this->data) { + return null; + } + + return new ValidationError('', $this, DataInfo::fromContext($context), 'Data not allowed'); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Schemas/EmptySchema.php b/vendor/opis/json-schema/src/Schemas/EmptySchema.php new file mode 100644 index 000000000..677f6b866 --- /dev/null +++ b/vendor/opis/json-schema/src/Schemas/EmptySchema.php @@ -0,0 +1,53 @@ +keywordValidator = $keywordValidator; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context): ?ValidationError + { + if (!$this->keywordValidator) { + return null; + } + + $context->pushSharedObject($this); + $error = $this->keywordValidator->validate($context); + $context->popSharedObject(); + + return $error; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Schemas/ExceptionSchema.php b/vendor/opis/json-schema/src/Schemas/ExceptionSchema.php new file mode 100644 index 000000000..967459742 --- /dev/null +++ b/vendor/opis/json-schema/src/Schemas/ExceptionSchema.php @@ -0,0 +1,48 @@ +exception = $exception; + } + + /** + * @inheritDoc + * @throws SchemaException + */ + public function validate(ValidationContext $context): ?ValidationError + { + throw $this->exception; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Schemas/LazySchema.php b/vendor/opis/json-schema/src/Schemas/LazySchema.php new file mode 100644 index 000000000..254d9bf62 --- /dev/null +++ b/vendor/opis/json-schema/src/Schemas/LazySchema.php @@ -0,0 +1,60 @@ +parser = $parser; + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context): ?ValidationError + { + return $this->schema()->validate($context); + } + + /** + * @return Schema + */ + public function schema(): Schema + { + if ($this->schema === null) { + $this->schema = $this->parser->parseSchema($this->info); + } + + return $this->schema; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Schemas/ObjectSchema.php b/vendor/opis/json-schema/src/Schemas/ObjectSchema.php new file mode 100644 index 000000000..1b5da2c9a --- /dev/null +++ b/vendor/opis/json-schema/src/Schemas/ObjectSchema.php @@ -0,0 +1,148 @@ +types = $types; + $this->before = $before; + $this->after = $after; + $this->keywordValidator = $keywordValidator; + + if ($keywordValidator) { + while ($next = $keywordValidator->next()) { + $keywordValidator = $next; + } + $keywordValidator->setNext(new CallbackKeywordValidator([$this, 'doValidate'])); + } + } + + /** + * @inheritDoc + */ + public function validate(ValidationContext $context): ?ValidationError + { + $context->pushSharedObject($this); + $error = $this->keywordValidator ? $this->keywordValidator->validate($context) : $this->doValidate($context); + $context->popSharedObject(); + + return $error; + } + + /** + * @param ValidationContext $context + * @return null|ValidationError + *@internal + */ + public function doValidate(ValidationContext $context): ?ValidationError + { + if ($this->before && ($error = $this->applyKeywords($this->before, $context))) { + return $error; + } + + if ($this->types && ($type = $context->currentDataType())) { + if (isset($this->types[$type]) && ($error = $this->applyKeywords($this->types[$type], $context))) { + return $error; + } + + if (($type = Helper::getJsonSuperType($type)) && isset($this->types[$type])) { + if ($error = $this->applyKeywords($this->types[$type], $context)) { + return $error; + } + } + + unset($type); + } + + if ($this->after && ($error = $this->applyKeywords($this->after, $context))) { + return $error; + } + + return null; + } + + /** + * @param Keyword[] $keywords + * @param ValidationContext $context + * @return ValidationError|null + */ + protected function applyKeywords(array $keywords, ValidationContext $context): ?ValidationError + { + if ($context->stopAtFirstError()) { + foreach ($keywords as $keyword) { + if ($error = $keyword->validate($context, $this)) { + return $error; + } + } + return null; + } + + /** @var null|ValidationError[] $error_list */ + $error_list = null; + + foreach ($keywords as $keyword) { + if ($error = $keyword->validate($context, $this)) { + $error_list ??= []; + $error_list[] = $error; + } + } + + if (!$error_list) { + return null; + } + + if (count($error_list) === 1) { + return $error_list[0]; + } + + return new ValidationError( + '', + $this, + DataInfo::fromContext($context), + 'Data must match schema', + [], + $error_list + ); + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Uri.php b/vendor/opis/json-schema/src/Uri.php new file mode 100644 index 000000000..3d6802fd7 --- /dev/null +++ b/vendor/opis/json-schema/src/Uri.php @@ -0,0 +1,94 @@ +__toString(); + } + + /** + * @param string $uri + * @param bool $ensure_fragment + * @return static|null + */ + public static function parse(string $uri, bool $ensure_fragment = false): ?self + { + if ($ensure_fragment && strpos($uri, '#') === false) { + $uri .= '#'; + } + + return self::create($uri); + } + + /** + * @param string|array|static $uri + * @param string|array|static $base + * @param bool $ensure_fragment + * @return static|null + */ + public static function merge($uri, $base, bool $ensure_fragment = false): ?self + { + $uri = self::resolveComponents($uri); + + if ($uri === null) { + return null; + } + + if ($ensure_fragment && !isset($uri['fragment'])) { + $uri['fragment'] = ''; + } + + $base = self::resolveComponents($base); + + if (!$base) { + return new self($uri); + } + + return new self(self::mergeComponents($uri, $base)); + } + + /** + * @param bool $value + */ + public static function useNormalizedComponents(bool $value): void + { + self::$useNormalizedComponents = $value; + } +} diff --git a/vendor/opis/json-schema/src/ValidationContext.php b/vendor/opis/json-schema/src/ValidationContext.php new file mode 100644 index 000000000..9e22b3e8f --- /dev/null +++ b/vendor/opis/json-schema/src/ValidationContext.php @@ -0,0 +1,715 @@ +sender = $sender; + $this->rootData = $data; + $this->loader = $loader; + $this->parent = $parent; + $this->globals = $globals; + $this->slots = null; + $this->maxErrors = $max_errors; + $this->stopAtFirstError = $stop_at_first_error; + $this->currentData = [ + [$data, false], + ]; + + if ($slots) { + $this->setSlots($slots); + } + } + + /** + * @param $data + * @param Schema|null $sender + * @param array|null $globals + * @param array|null $slots + * @param int|null $max_errors + * @return self + */ + public function newInstance( + $data, + ?Schema $sender, + ?array $globals = null, + ?array $slots = null, + ?int $max_errors = null, + ?bool $stop_at_first_error = null + ): self { + return new self( + $data, + $this->loader, + $this, + $sender, + $globals ?? $this->globals, + $slots ?? $this->slots, + $max_errors ?? $this->maxErrors, + $stop_at_first_error ?? $this->stopAtFirstError + ); + } + + public function create( + Schema $sender, + ?Variables $mapper = null, + ?Variables $globals = null, + ?array $slots = null, + ?int $maxErrors = null, + ?bool $stop_at_first_error = null + ): self { + if ($globals) { + $globals = $globals->resolve($this->rootData(), $this->currentDataPath()); + if (!is_array($globals)) { + $globals = (array)$globals; + } + $globals += $this->globals; + } else { + $globals = $this->globals; + } + + if ($mapper) { + $data = $mapper->resolve($this->rootData(), $this->currentDataPath()); + } else { + $data = $this->currentData(); + } + + return new self($data, $this->loader, $this, $sender, $globals, $slots ?? $this->slots, + $maxErrors ?? $this->maxErrors, $stop_at_first_error ?? $this->stopAtFirstError); + } + + public function sender(): ?Schema + { + return $this->sender; + } + + /** + * @return self|null + */ + public function parent(): ?self + { + return $this->parent; + } + + /** + * @return SchemaLoader + */ + public function loader(): SchemaLoader + { + return $this->loader; + } + + /** + * @return mixed + */ + public function rootData() + { + return $this->rootData; + } + + /** + * @return mixed + */ + public function currentData() + { + return $this->currentData[$this->pathIndex][0]; + } + + /** + * @param $value + */ + public function setCurrentData($value): void + { + $this->currentData[$this->pathIndex][0] = $value; + $this->currentData[$this->pathIndex][1] = false; + } + + /** + * @return string|null + */ + public function currentDataType(): ?string + { + $type = $this->currentData[$this->pathIndex][1]; + if ($type === false) { + $type = Helper::getJsonType($this->currentData[$this->pathIndex][0]); + $this->currentData[$this->pathIndex][1] = $type; + } + + return $type; + } + + public function fullDataPath(): array + { + if ($this->fullPath === null) { + if ($this->parent === null) { + return $this->currentDataPath; + } + $this->fullPath = array_merge($this->parent->fullDataPath(), $this->currentDataPath); + } + + return $this->fullPath; + } + + /** + * @return int[]|string[] + */ + public function currentDataPath(): array + { + return $this->currentDataPath; + } + + /** + * @param string|int $key + * @return $this + */ + public function pushDataPath($key): self + { + $this->currentDataPath[] = $key; + if ($this->fullPath !== null) { + $this->fullPath[] = $key; + } + + $data = $this->currentData[$this->pathIndex][0]; + + if (is_array($data)) { + $data = $data[$key] ?? null; + } elseif (is_object($data)) { + $data = $data->{$key} ?? null; + } else { + $data = null; + } + + $this->currentData[] = [$data, false]; + $this->pathIndex++; + + return $this; + } + + /** + * @return $this + */ + public function popDataPath(): self + { + if ($this->pathIndex < 1) { + return $this; + } + + if ($this->fullPath !== null) { + array_pop($this->fullPath); + } + array_pop($this->currentDataPath); + array_pop($this->currentData); + $this->pathIndex--; + + return $this; + } + + /** + * @return array + */ + public function globals(): array + { + return $this->globals; + } + + /** + * @param array $globals + * @param bool $overwrite + * @return $this + */ + public function setGlobals(array $globals, bool $overwrite = false): self + { + if ($overwrite) { + $this->globals = $globals; + } elseif ($globals) { + $this->globals = $globals + $this->globals; + } + + return $this; + } + + /** + * @return object[]|Schema[]|string[]|null + */ + public function slots(): ?array + { + return $this->slots; + } + + /** + * @param array|null $slots + * @return $this + */ + public function setSlots(?array $slots): self + { + if ($slots) { + $list = []; + + foreach ($slots as $name => $value) { + if (is_bool($value)) { + $value = $this->loader->loadBooleanSchema($value); + } elseif (is_object($value)) { + if ($value instanceof Schema) { + $list[$name] = $value; + continue; + } + $value = $this->loader->loadObjectSchema($value); + } elseif (is_string($value)) { + if (isset($this->slots[$value])) { + $value = $this->slots[$value]; + } elseif ($this->parent) { + $value = $this->parent->slot($value); + } + } + + if ($value instanceof Schema) { + $list[$name] = $value; + } + } + + $this->slots = $list; + } else { + $this->slots = null; + } + + return $this; + } + + /** + * @param string $name + * @return Schema|null + */ + public function slot(string $name): ?Schema + { + return $this->slots[$name] ?? null; + } + + /** + * @return int + */ + public function maxErrors(): int + { + return $this->maxErrors; + } + + /** + * @param int $max + * @return $this + */ + public function setMaxErrors(int $max): self + { + $this->maxErrors = $max; + + return $this; + } + + + public function stopAtFirstError(): bool + { + return $this->stopAtFirstError; + } + + public function setStopAtFirstError(bool $stop): self + { + $this->stopAtFirstError = $stop; + + return $this; + } + + /* --------------------- */ + + /** + * @param Schema $schema + * @return $this + */ + public function pushSharedObject(Schema $schema): self + { + $unevaluated = !in_array($schema->info()->draft(), ['06', '07']); + if ($unevaluated && ($parser = $this->loader->parser()) && !$parser->option('allowUnevaluated', true)) { + $unevaluated = false; + } + + $this->shared[] = [ + 'schema' => $schema, + 'unevaluated' => $unevaluated, + 'object' => null, + ]; + $this->sharedIndex++; + + return $this; + } + + /** + * @return $this + */ + public function popSharedObject(): self + { + if ($this->sharedIndex < 0) { + return $this; + } + + $data = array_pop($this->shared); + $this->sharedIndex--; + + if ($data['unevaluated'] && $data['object']) { + if ($this->sharedIndex >= 0) { + $this->mergeUnevaluated($data['object']); + } elseif ($this->parent && $this->parent->sharedIndex >= 0) { + $this->parent->mergeUnevaluated($data['object']); + } + } + + return $this; + } + + /** + * @return object|null + */ + public function sharedObject(): ?object + { + if ($this->sharedIndex < 0) { + return null; + } + + return $this->shared[$this->sharedIndex]['object'] ??= (object)[]; + } + + public function schema(): ?Schema + { + return $this->shared[$this->sharedIndex]['schema'] ?? null; + } + + public function trackUnevaluated(): bool + { + return $this->shared[$this->sharedIndex]['unevaluated'] ?? false; + } + + protected function mergeUnevaluated(object $obj): void + { + switch ($this->currentDataType()) { + case 'object': + if (isset($obj->evaluatedProperties)) { + $this->addEvaluatedProperties($obj->evaluatedProperties); + } + break; + case 'array': + if (isset($obj->evaluatedItems)) { + $this->addEvaluatedItems($obj->evaluatedItems); + } + break; + } + } + + /* ----------------*/ + + public function getStringLength(): ?int + { + if ($this->currentDataType() !== 'string') { + return null; + } + + $shared = $this->sharedObject(); + + if (!isset($shared->stringLength)) { + $shared->stringLength = UnicodeString::from($this->currentData())->length(); + } + + return $shared->stringLength; + } + + public function setDecodedContent(string $content): bool + { + if ($this->currentDataType() !== 'string') { + return false; + } + + $this->sharedObject()->decodedContent = $content; + + return true; + } + + public function getDecodedContent(): ?string + { + if ($this->currentDataType() !== 'string') { + return null; + } + return $this->sharedObject()->decodedContent ?? $this->currentData(); + } + + public function getObjectProperties(): ?array + { + if ($this->currentDataType() !== 'object') { + return null; + } + + return $this->sharedObject()->objectProperties ??= array_keys(get_object_vars($this->currentData())); + } + + public function addCheckedProperties(?array $properties): bool + { + if (!$properties) { + return false; + } + + $shared = $this->sharedObject(); + + if (!isset($shared->checkedProperties)) { + $shared->checkedProperties = $properties; + } else { + $shared->checkedProperties = array_values(array_unique(array_merge($shared->checkedProperties, $properties))); + } + + return true; + } + + public function getCheckedProperties(): ?array + { + return $this->sharedObject()->checkedProperties ?? null; + } + + public function getUncheckedProperties(): ?array + { + $properties = $this->getObjectProperties(); + if (!$properties) { + return $properties; + } + + $checked = $this->sharedObject()->checkedProperties ?? null; + if (!$checked) { + return $properties; + } + + return array_values(array_diff($properties, $checked)); + } + + public function markAllAsEvaluatedProperties(): bool + { + return $this->addEvaluatedProperties($this->getObjectProperties()); + } + + public function addEvaluatedProperties(?array $properties): bool + { + if (!$properties || !($this->currentDataType() === 'object') || !$this->trackUnevaluated()) { + return false; + } + + $shared = $this->sharedObject(); + + if (!isset($shared->evaluatedProperties)) { + $shared->evaluatedProperties = $properties; + } else { + $shared->evaluatedProperties = array_values(array_unique(array_merge($shared->evaluatedProperties, $properties))); + } + + return true; + } + + public function getEvaluatedProperties(): ?array + { + return $this->sharedObject()->evaluatedProperties ?? null; + } + + public function getUnevaluatedProperties(): ?array + { + $properties = $this->getObjectProperties(); + if (!$properties) { + return $properties; + } + + $evaluated = $this->sharedObject()->evaluatedProperties ?? null; + if (!$evaluated) { + return $properties; + } + + return array_values(array_diff($properties, $evaluated)); + } + + public function markAllAsEvaluatedItems(): bool + { + return $this->addEvaluatedItems(range(0, count($this->currentData()))); + } + + public function markCountAsEvaluatedItems(int $count): bool + { + if (!$count) { + return false; + } + + return $this->addEvaluatedItems(range(0, $count)); + } + + public function addEvaluatedItems(?array $items): bool + { + if (!$items || !($this->currentDataType() === 'array') || !$this->trackUnevaluated()) { + return false; + } + + $shared = $this->sharedObject(); + + if (!isset($shared->evaluatedItems)) { + $shared->evaluatedItems = $items; + } else { + $shared->evaluatedItems = array_values(array_unique(array_merge($shared->evaluatedItems, $items), SORT_NUMERIC)); + } + + return true; + } + + public function getEvaluatedItems(): ?array + { + return $this->sharedObject()->evaluatedItems ?? null; + } + + public function getUnevaluatedItems(): ?array + { + if ($this->currentDataType() !== 'array') { + return null; + } + + $items = array_keys($this->currentData()); + if (!$items) { + return $items; + } + + $evaluated = $this->sharedObject()->evaluatedItems ?? null; + if (!$evaluated) { + return $items; + } + + return array_values(array_diff($items, $evaluated)); + } + + public function validateSchemaWithoutEvaluated( + Schema $schema, + ?int $maxErrors = null, + bool $reset_on_error_only = false, + ?ArrayObject $array = null + ): ?ValidationError { + $currentMaxErrors = $this->maxErrors; + + $this->maxErrors = $maxErrors ?? $currentMaxErrors; + + if ($this->trackUnevaluated()) { + $shared = $this->sharedObject(); + + $props = $shared->evaluatedProperties ?? null; + $items = $shared->evaluatedItems ?? null; + + $error = $schema->validate($this); + + if ($array) { + $value = null; + + if ($shared->evaluatedProperties ?? null) { + if ($props) { + if ($diff = array_diff($shared->evaluatedProperties, $props)) { + $value['properties'] = $diff; + } + } else { + $value['properties'] = $shared->evaluatedProperties; + } + } + + if ($shared->evaluatedItems ?? null) { + if ($items) { + if ($diff = array_diff($shared->evaluatedItems, $items)) { + $value['items'] = $diff; + } + } else { + $value['items'] = $shared->evaluatedItems; + } + } + + if ($value) { + $array[] = $value; + } + } + + if ($reset_on_error_only) { + if ($error) { + $shared->evaluatedProperties = $props; + $shared->evaluatedItems = $items; + } + } else { + $shared->evaluatedProperties = $props; + $shared->evaluatedItems = $items; + } + } else { + $error = $schema->validate($this); + } + + $this->maxErrors = $currentMaxErrors; + + return $error; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/ValidationResult.php b/vendor/opis/json-schema/src/ValidationResult.php new file mode 100644 index 000000000..08ca63f4b --- /dev/null +++ b/vendor/opis/json-schema/src/ValidationResult.php @@ -0,0 +1,53 @@ +error = $error; + } + + public function error(): ?ValidationError + { + return $this->error; + } + + public function isValid(): bool + { + return $this->error === null; + } + + public function hasError(): bool + { + return $this->error !== null; + } + + public function __toString(): string + { + if ($this->error) { + return $this->error->message(); + } + return ''; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Validator.php b/vendor/opis/json-schema/src/Validator.php new file mode 100644 index 000000000..f80a70b35 --- /dev/null +++ b/vendor/opis/json-schema/src/Validator.php @@ -0,0 +1,317 @@ +loader = $loader ?? new SchemaLoader(new SchemaParser(), new SchemaResolver(), true); + $this->maxErrors = $max_errors; + $this->stopAtFirstError = $stop_at_first_error; + } + + /** + * @param $data + * @param bool|string|Uri|Schema|object $schema + * @param array|null $globals + * @param array|null $slots + * @return ValidationResult + */ + public function validate($data, $schema, ?array $globals = null, ?array $slots = null): ValidationResult + { + if (is_string($schema)) { + if ($uri = Uri::parse($schema, true)) { + $schema = $uri; + } else { + $schema = json_decode($schema, false); + } + } + + $error = null; + if (is_bool($schema)) { + $error = $this->dataValidation($data, $schema, $globals, $slots); + } elseif (is_object($schema)) { + if ($schema instanceof Uri) { + $error = $this->uriValidation($data, $schema, $globals, $slots); + } elseif ($schema instanceof Schema) { + $error = $this->schemaValidation($data, $schema, $globals, $slots); + } else { + $error = $this->dataValidation($data, $schema, $globals, $slots); + } + } else { + throw new InvalidArgumentException("Invalid schema"); + } + + return new ValidationResult($error); + } + + /** + * @param $data + * @param Uri|string $uri + * @param array|null $globals + * @param array|null $slots + * @return null|ValidationError + */ + public function uriValidation($data, $uri, ?array $globals = null, ?array $slots = null): ?ValidationError + { + if (is_string($uri)) { + $uri = Uri::parse($uri, true); + } + + if (!($uri instanceof Uri)) { + throw new InvalidArgumentException("Invalid uri"); + } + + if ($uri->fragment() === null) { + $uri = Uri::merge($uri, null, true); + } + + $schema = $this->loader->loadSchemaById($uri); + + if ($schema === null) { + throw new RuntimeException("Schema not found: $uri"); + } + + return $this->schemaValidation($data, $schema, $globals, $slots); + } + + /** + * @param $data + * @param string|object|bool $schema + * @param array|null $globals + * @param array|null $slots + * @param string|null $id + * @param string|null $draft + * @return ValidationError|null + */ + public function dataValidation( + $data, + $schema, + ?array $globals = null, + ?array $slots = null, + ?string $id = null, + ?string $draft = null + ): ?ValidationError + { + if (is_string($schema)) { + $schema = json_decode($schema, false); + } + + if ($schema === true) { + return null; + } + + if ($schema === false) { + $schema = $this->loader->loadBooleanSchema(false, $id, $draft); + } else { + if (!is_object($schema)) { + throw new InvalidArgumentException("Invalid schema"); + } + + $schema = $this->loader->loadObjectSchema($schema, $id, $draft); + } + + return $this->schemaValidation($data, $schema, $globals, $slots); + } + + /** + * @param $data + * @param Schema $schema + * @param array|null $globals + * @param array|null $slots + * @return null|ValidationError + */ + public function schemaValidation( + $data, + Schema $schema, + ?array $globals = null, + ?array $slots = null + ): ?ValidationError + { + return $schema->validate($this->createContext($data, $globals, $slots)); + } + + /** + * @param $data + * @param array|null $globals + * @param array|null $slots + * @return ValidationContext + */ + public function createContext($data, ?array $globals = null, ?array $slots = null): ValidationContext + { + if ($slots) { + $slots = $this->parseSlots($slots); + } + + return new ValidationContext( + $data, + $this->loader, + null, + null, + $globals ?? [], + $slots, + $this->maxErrors, + $this->stopAtFirstError, + ); + } + + /** + * @return SchemaParser + */ + public function parser(): SchemaParser + { + return $this->loader->parser(); + } + + /** + * @param SchemaParser $parser + * @return Validator + */ + public function setParser(SchemaParser $parser): self + { + $this->loader->setParser($parser); + + return $this; + } + + /** + * @return SchemaResolver|null + */ + public function resolver(): ?SchemaResolver + { + return $this->loader->resolver(); + } + + /** + * @param SchemaResolver|null $resolver + * @return Validator + */ + public function setResolver(?SchemaResolver $resolver): self + { + $this->loader->setResolver($resolver); + + return $this; + } + + /** + * @return SchemaLoader + */ + public function loader(): SchemaLoader + { + return $this->loader; + } + + /** + * @param SchemaLoader $loader + * @return Validator + */ + public function setLoader(SchemaLoader $loader): self + { + $this->loader = $loader; + + return $this; + } + + /** + * @return int + */ + public function getMaxErrors(): int + { + return $this->maxErrors; + } + + /** + * @param int $max_errors + * @return Validator + */ + public function setMaxErrors(int $max_errors): self + { + $this->maxErrors = $max_errors; + + return $this; + } + + /** + * @return bool + */ + public function getStopAtFirstError(): bool + { + return $this->stopAtFirstError; + } + + /** + * @param bool $stop + * @return $this + */ + public function setStopAtFirstError(bool $stop): self + { + $this->stopAtFirstError = $stop; + return $this; + } + + /** + * @param array $slots + * @return array + */ + protected function parseSlots(array $slots): array + { + foreach ($slots as $name => &$value) { + if (!is_string($name)) { + unset($slots[$name]); + continue; + } + + if (is_string($value)) { + $value = Uri::parse($value, true); + } + + if ($value instanceof Uri) { + $value = $this->loader->loadSchemaById($value); + } elseif (is_bool($value)) { + $value = $this->loader->loadBooleanSchema($value); + } + + if (!is_object($value)) { + unset($slots[$name]); + } + + unset($value); + } + + return $slots; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Variables.php b/vendor/opis/json-schema/src/Variables.php new file mode 100644 index 000000000..a2682cb54 --- /dev/null +++ b/vendor/opis/json-schema/src/Variables.php @@ -0,0 +1,28 @@ +pointer = $pointer; + $this->each = $each; + $this->hasDefault = func_num_args() === 3; + $this->defaultValue = $default; + } + + /** + * @return JsonPointer + */ + public function pointer(): JsonPointer + { + return $this->pointer; + } + + /** + * @return null|Variables + */ + public function each(): ?Variables + { + return $this->each; + } + + /** + * @return bool + */ + public function hasDefaultValue(): bool + { + return $this->hasDefault; + } + + /** + * @return mixed|null + */ + public function defaultValue() + { + return $this->defaultValue; + } + + /** + * @inheritDoc + */ + public function resolve($data, array $path = []) + { + $resolved = $this->pointer->data($data, $path, $this); + if ($resolved === $this) { + return $this->defaultValue; + } + + if ($this->each && (is_array($resolved) || is_object($resolved))) { + $path = $this->pointer->absolutePath($path); + foreach ($resolved as $key => &$value) { + $path[] = $key; + $value = $this->each->resolve($data, $path); + array_pop($path); + unset($value); + } + } + + return $resolved; + } +} \ No newline at end of file diff --git a/vendor/opis/json-schema/src/Variables/VariablesContainer.php b/vendor/opis/json-schema/src/Variables/VariablesContainer.php new file mode 100644 index 000000000..163c9b517 --- /dev/null +++ b/vendor/opis/json-schema/src/Variables/VariablesContainer.php @@ -0,0 +1,162 @@ +keys = [ + 'ref' => $ref_key, + 'each' => $each_key, + 'default' => $default_key, + ]; + + if ($lazy) { + $this->vars = $data; + } else { + $this->parsed = true; + $this->vars = $this->parse($data); + } + } + + /** + * @inheritdoc + */ + public function resolve($data, array $path = []) + { + if (!$this->parsed) { + $this->vars = $this->parse($this->vars); + $this->parsed = true; + } + + if (!$this->hasRefs) { + // Nothing to resolve + return $this->vars; + } + + return $this->deepClone($this->vars, $data, $path); + } + + /** + * @param $vars + * @param $data + * @param string[]|int[] $path + * @return array|object|mixed + */ + private function deepClone($vars, $data, array $path) + { + $toObject = false; + if (is_object($vars)) { + if ($vars instanceof Variables) { + return $vars->resolve($data, $path); + } + $vars = get_object_vars($vars); + $toObject = true; + } elseif (!is_array($vars)) { + return $vars; + } + + foreach ($vars as &$var) { + if ($var !== null && !is_scalar($var)) { + $var = $this->deepClone($var, $data, $path); + } + unset($var); + } + + return $toObject ? (object)$vars : $vars; + } + + /** + * @param mixed $data + * @return mixed + */ + private function parse($data) + { + if (is_array($data)) { + return array_map([$this, 'parse'], $data); + } + + if (!is_object($data)) { + return $data; + } + + if ($vars = $this->parseRef($data)) { + $this->hasRefs = true; + + return $vars; + } + + return (object)array_map([$this, 'parse'], get_object_vars($data)); + } + + /** + * @param object $data + * @return null|Variables + */ + private function parseRef(object $data): ?Variables + { + if (!property_exists($data, $this->keys['ref'])) { + return null; + } + + $ref = $data->{$this->keys['ref']}; + if (!is_string($ref)) { + return null; + } + + $pointer = JsonPointer::parse($ref); + if ($pointer === null) { + return null; + } + + $each = null; + if (property_exists($data, $this->keys['each']) && is_object($data->{$this->keys['each']})) { + $each = new self($data->{$this->keys['each']}, !$this->parsed, $this->keys['ref'], $this->keys['each'], $this->keys['default']); + } + + if (property_exists($data, $this->keys['default'])) { + return new RefVariablesContainer($pointer, $each, $data->{$this->keys['default']}); + } + + return new RefVariablesContainer($pointer, $each); + } +} \ No newline at end of file diff --git a/vendor/opis/string/.editorconfig b/vendor/opis/string/.editorconfig new file mode 100644 index 000000000..131e7a277 --- /dev/null +++ b/vendor/opis/string/.editorconfig @@ -0,0 +1,8 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +charset = utf-8 diff --git a/vendor/opis/string/CHANGELOG.md b/vendor/opis/string/CHANGELOG.md new file mode 100644 index 000000000..32fc72149 --- /dev/null +++ b/vendor/opis/string/CHANGELOG.md @@ -0,0 +1,6 @@ +CHANGELOG +------------- + +### v2.0.0, 2021.04.13 + +* The library was fully refactored diff --git a/vendor/opis/string/LICENSE b/vendor/opis/string/LICENSE new file mode 100644 index 000000000..d9a10c0d8 --- /dev/null +++ b/vendor/opis/string/LICENSE @@ -0,0 +1,176 @@ + 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 diff --git a/vendor/opis/string/NOTICE b/vendor/opis/string/NOTICE new file mode 100644 index 000000000..d63600ed7 --- /dev/null +++ b/vendor/opis/string/NOTICE @@ -0,0 +1,9 @@ +Opis String +Copyright 2018-2021 Zindex Software + +This product includes software developed at +Zindex Software (http://zindex.software). + +This software was originally developed by Marius Sarca and Sorin Sarca +(Copyright 2016-2018). The copyright info was changed with the permission +of the original authors. diff --git a/vendor/opis/string/README.md b/vendor/opis/string/README.md new file mode 100644 index 000000000..8bca7c7b8 --- /dev/null +++ b/vendor/opis/string/README.md @@ -0,0 +1,50 @@ +Opis String +=========== +[![Tests](https://github.com/opis/string/workflows/Tests/badge.svg)](https://github.com/opis/string/actions) +[![Latest Stable Version](https://poser.pugx.org/opis/string/version.png)](https://packagist.org/packages/opis/string) +[![Latest Unstable Version](https://poser.pugx.org/opis/string/v/unstable.png)](https://packagist.org/packages/opis/string) +[![License](https://poser.pugx.org/opis/string/license.png)](https://packagist.org/packages/opis/string) + +Multibyte strings +---------------------------- + +**Opis String** is a tiny library that allows you to work with multibyte encoded strings in an object-oriented manner. +The library has no dependencies to *mb_string* or similar PHP extensions. + +## Documentation + +The full documentation for this library can be found [here][documentation]. + +## License + +**Opis String** is licensed under the [Apache License, Version 2.0][license]. + +## Requirements + +* PHP ^7.4 || ^8.0 +* ext-json +* ext-iconv + +## Installation + +**Opis String** is available on [Packagist] and it can be installed from a +command line interface by using [Composer]. + +```bash +composer require opis/string +``` + +Or you could directly reference it into your `composer.json` file as a dependency + +```json +{ + "require": { + "opis/string": "^2.0" + } +} +``` + +[documentation]: https://opis.io/string +[license]: https://www.apache.org/licenses/LICENSE-2.0 "Apache License" +[Packagist]: https://packagist.org/packages/opis/string "Packagist" +[Composer]: https://getcomposer.org "Composer" diff --git a/vendor/opis/string/composer.json b/vendor/opis/string/composer.json new file mode 100644 index 000000000..0b96c635d --- /dev/null +++ b/vendor/opis/string/composer.json @@ -0,0 +1,40 @@ +{ + "name": "opis/string", + "description": "Multibyte strings as objects", + "keywords": ["opis", "string", "utf-8", "multi-byte", "string manipulation"], + "homepage": "https://opis.io/string", + "license": "Apache-2.0", + "authors": [ + { + "name": "Marius Sarca", + "email": "marius.sarca@gmail.com" + }, + { + "name": "Sorin Sarca", + "email": "sarca_sorin@hotmail.com" + } + ], + "require": { + "php": "^7.4 || ^8.0", + "ext-json": "*", + "ext-iconv": "*" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "autoload": { + "psr-4": { + "Opis\\String\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Opis\\String\\Test\\": "tests/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + } +} diff --git a/vendor/opis/string/res/ascii.php b/vendor/opis/string/res/ascii.php new file mode 100644 index 000000000..000a19fc7 --- /dev/null +++ b/vendor/opis/string/res/ascii.php @@ -0,0 +1,751 @@ + 0x28, +0xAA => 0x61, +0xB0 => 0x30, +0xB2 => 0x32, +0xB3 => 0x33, +0xB5 => 0x75, +0xB9 => 0x31, +0xBA => 0x6F, +0xC0 => 0x41, +0xC1 => 0x41, +0xC2 => 0x41, +0xC3 => 0x41, +0xC4 => 0x41, +0xC5 => 0x41, +0xC6 => 0x41, +0xC7 => 0x43, +0xC8 => 0x45, +0xC9 => 0x45, +0xCA => 0x45, +0xCB => 0x45, +0xCC => 0x49, +0xCD => 0x49, +0xCE => 0x49, +0xCF => 0x49, +0xD0 => 0x44, +0xD1 => 0x4E, +0xD2 => 0x4F, +0xD3 => 0x4F, +0xD4 => 0x4F, +0xD5 => 0x4F, +0xD6 => 0x4F, +0xD8 => 0x4F, +0xD9 => 0x55, +0xDA => 0x55, +0xDB => 0x55, +0xDC => 0x55, +0xDD => 0x59, +0xDE => 0x54, +0xDF => 0x73, +0xE0 => 0x61, +0xE1 => 0x61, +0xE2 => 0x61, +0xE3 => 0x61, +0xE4 => 0x61, +0xE5 => 0x61, +0xE6 => 0x61, +0xE7 => 0x63, +0xE8 => 0x65, +0xE9 => 0x65, +0xEA => 0x65, +0xEB => 0x65, +0xEC => 0x69, +0xED => 0x69, +0xEE => 0x69, +0xEF => 0x69, +0xF0 => 0x64, +0xF1 => 0x6E, +0xF2 => 0x6F, +0xF3 => 0x6F, +0xF4 => 0x6F, +0xF5 => 0x6F, +0xF6 => 0x6F, +0xF8 => 0x6F, +0xF9 => 0x75, +0xFA => 0x75, +0xFB => 0x75, +0xFC => 0x75, +0xFD => 0x79, +0xFE => 0x74, +0xFF => 0x79, +0x100 => 0x41, +0x101 => 0x61, +0x102 => 0x41, +0x103 => 0x61, +0x104 => 0x41, +0x105 => 0x61, +0x106 => 0x43, +0x107 => 0x63, +0x108 => 0x43, +0x109 => 0x63, +0x10A => 0x43, +0x10B => 0x63, +0x10C => 0x43, +0x10D => 0x63, +0x10E => 0x44, +0x10F => 0x64, +0x110 => 0x44, +0x111 => 0x64, +0x112 => 0x45, +0x113 => 0x65, +0x114 => 0x45, +0x115 => 0x65, +0x116 => 0x45, +0x117 => 0x65, +0x118 => 0x45, +0x119 => 0x65, +0x11A => 0x45, +0x11B => 0x65, +0x11C => 0x47, +0x11D => 0x67, +0x11E => 0x47, +0x11F => 0x67, +0x120 => 0x47, +0x121 => 0x67, +0x122 => 0x47, +0x123 => 0x67, +0x124 => 0x48, +0x125 => 0x68, +0x126 => 0x48, +0x127 => 0x68, +0x128 => 0x49, +0x129 => 0x69, +0x12A => 0x49, +0x12B => 0x69, +0x12C => 0x49, +0x12D => 0x69, +0x12E => 0x49, +0x12F => 0x69, +0x130 => 0x49, +0x131 => 0x69, +0x132 => 0x49, +0x133 => 0x69, +0x134 => 0x4A, +0x135 => 0x6A, +0x136 => 0x6B, +0x137 => 0x6B, +0x138 => 0x6B, +0x139 => 0x4C, +0x13A => 0x6C, +0x13B => 0x4C, +0x13C => 0x6C, +0x13D => 0x4C, +0x13E => 0x6C, +0x13F => 0x4C, +0x140 => 0x6C, +0x141 => 0x4C, +0x142 => 0x6C, +0x143 => 0x4E, +0x144 => 0x6E, +0x145 => 0x4E, +0x146 => 0x6E, +0x147 => 0x4E, +0x148 => 0x6E, +0x149 => 0x6E, +0x14A => 0x4E, +0x14B => 0x6E, +0x14C => 0x4F, +0x14D => 0x6F, +0x14E => 0x4F, +0x14F => 0x6F, +0x150 => 0x4F, +0x151 => 0x6F, +0x152 => 0x4F, +0x153 => 0x6F, +0x154 => 0x52, +0x155 => 0x72, +0x156 => 0x52, +0x157 => 0x72, +0x158 => 0x52, +0x159 => 0x72, +0x15A => 0x53, +0x15B => 0x73, +0x15C => 0x53, +0x15D => 0x73, +0x15E => 0x53, +0x15F => 0x73, +0x160 => 0x53, +0x161 => 0x73, +0x162 => 0x54, +0x163 => 0x74, +0x164 => 0x54, +0x165 => 0x74, +0x166 => 0x54, +0x167 => 0x74, +0x168 => 0x55, +0x169 => 0x75, +0x16A => 0x55, +0x16B => 0x75, +0x16C => 0x55, +0x16D => 0x75, +0x16E => 0x55, +0x16F => 0x75, +0x170 => 0x55, +0x171 => 0x75, +0x172 => 0x55, +0x173 => 0x75, +0x174 => 0x57, +0x175 => 0x77, +0x176 => 0x59, +0x177 => 0x79, +0x178 => 0x59, +0x179 => 0x5A, +0x17A => 0x7A, +0x17B => 0x5A, +0x17C => 0x7A, +0x17D => 0x5A, +0x17E => 0x7A, +0x17F => 0x73, +0x189 => 0x44, +0x18A => 0x44, +0x18B => 0x44, +0x18C => 0x64, +0x18F => 0x45, +0x192 => 0x66, +0x1A0 => 0x4F, +0x1A1 => 0x6F, +0x1AF => 0x55, +0x1B0 => 0x75, +0x1CD => 0x41, +0x1CE => 0x61, +0x1CF => 0x49, +0x1D0 => 0x69, +0x1D1 => 0x4F, +0x1D2 => 0x6F, +0x1D3 => 0x55, +0x1D4 => 0x75, +0x1D5 => 0x55, +0x1D6 => 0x75, +0x1D7 => 0x55, +0x1D8 => 0x75, +0x1D9 => 0x55, +0x1DA => 0x75, +0x1DB => 0x55, +0x1DC => 0x75, +0x1FA => 0x41, +0x1FB => 0x61, +0x1FC => 0x41, +0x1FD => 0x61, +0x1FE => 0x4F, +0x1FF => 0x6F, +0x218 => 0x53, +0x219 => 0x73, +0x21A => 0x54, +0x21B => 0x74, +0x221 => 0x64, +0x256 => 0x64, +0x257 => 0x64, +0x259 => 0x65, +0x386 => 0x41, +0x388 => 0x45, +0x389 => 0x48, +0x38A => 0x49, +0x38C => 0x4F, +0x38E => 0x59, +0x38F => 0x57, +0x390 => 0x69, +0x391 => 0x41, +0x392 => 0x42, +0x393 => 0x47, +0x394 => 0x44, +0x395 => 0x45, +0x396 => 0x5A, +0x397 => 0x48, +0x398 => 0x4F, +0x399 => 0x49, +0x39A => 0x4B, +0x39B => 0x4C, +0x39C => 0x4D, +0x39D => 0x4E, +0x39E => 0x58, +0x39F => 0x4F, +0x3A0 => 0x50, +0x3A1 => 0x52, +0x3A3 => 0x53, +0x3A4 => 0x54, +0x3A5 => 0x59, +0x3A6 => 0x46, +0x3A7 => 0x58, +0x3A8 => 0x50, +0x3A9 => 0x57, +0x3AA => 0x49, +0x3AB => 0x59, +0x3AC => 0x61, +0x3AD => 0x65, +0x3AE => 0x68, +0x3AF => 0x69, +0x3B0 => 0x79, +0x3B1 => 0x61, +0x3B2 => 0x62, +0x3B3 => 0x67, +0x3B4 => 0x64, +0x3B5 => 0x65, +0x3B6 => 0x7A, +0x3B7 => 0x68, +0x3B8 => 0x6F, +0x3B9 => 0x69, +0x3BA => 0x6B, +0x3BB => 0x6C, +0x3BC => 0x6D, +0x3BD => 0x6E, +0x3BE => 0x78, +0x3BF => 0x6F, +0x3C0 => 0x70, +0x3C1 => 0x72, +0x3C2 => 0x73, +0x3C3 => 0x73, +0x3C4 => 0x74, +0x3C5 => 0x79, +0x3C6 => 0x66, +0x3C7 => 0x78, +0x3C8 => 0x70, +0x3C9 => 0x77, +0x3CA => 0x69, +0x3CB => 0x79, +0x3CC => 0x6F, +0x3CD => 0x79, +0x3CE => 0x77, +0x3D0 => 0x76, +0x3D1 => 0x74, +0x3D2 => 0x49, +0x401 => 0x45, +0x402 => 0x44, +0x404 => 0x45, +0x406 => 0x49, +0x407 => 0x49, +0x408 => 0x6A, +0x409 => 0x4C, +0x40A => 0x4E, +0x40F => 0x44, +0x410 => 0x41, +0x411 => 0x42, +0x412 => 0x56, +0x413 => 0x47, +0x414 => 0x44, +0x415 => 0x45, +0x416 => 0x5A, +0x417 => 0x5A, +0x418 => 0x49, +0x419 => 0x59, +0x41A => 0x4B, +0x41B => 0x4C, +0x41C => 0x4D, +0x41D => 0x4E, +0x41E => 0x4F, +0x41F => 0x50, +0x420 => 0x52, +0x421 => 0x53, +0x422 => 0x54, +0x423 => 0x55, +0x424 => 0x46, +0x425 => 0x4B, +0x426 => 0x54, +0x427 => 0x43, +0x428 => 0x53, +0x429 => 0x53, +0x42A => 0x62, +0x42B => 0x59, +0x42C => 0x62, +0x42D => 0x45, +0x42E => 0x59, +0x42F => 0x59, +0x430 => 0x61, +0x431 => 0x62, +0x432 => 0x76, +0x433 => 0x67, +0x434 => 0x64, +0x435 => 0x65, +0x436 => 0x7A, +0x437 => 0x7A, +0x438 => 0x69, +0x439 => 0x79, +0x43A => 0x6B, +0x43B => 0x6C, +0x43C => 0x6D, +0x43D => 0x6E, +0x43E => 0x6F, +0x43F => 0x70, +0x440 => 0x72, +0x441 => 0x73, +0x442 => 0x74, +0x443 => 0x75, +0x444 => 0x66, +0x445 => 0x6B, +0x446 => 0x74, +0x447 => 0x63, +0x448 => 0x73, +0x449 => 0x73, +0x44B => 0x79, +0x44D => 0x65, +0x44E => 0x79, +0x44F => 0x79, +0x451 => 0x65, +0x452 => 0x64, +0x454 => 0x65, +0x456 => 0x69, +0x457 => 0x69, +0x458 => 0x6A, +0x459 => 0x6C, +0x45A => 0x6E, +0x45F => 0x64, +0x490 => 0x47, +0x491 => 0x67, +0x4E8 => 0x4F, +0x622 => 0x61, +0x623 => 0x61, +0x624 => 0x6F, +0x625 => 0x65, +0x626 => 0x65, +0x627 => 0x61, +0x628 => 0x62, +0x62A => 0x74, +0x62B => 0x74, +0x62C => 0x6A, +0x62D => 0x68, +0x62E => 0x6B, +0x62F => 0x64, +0x630 => 0x74, +0x631 => 0x72, +0x632 => 0x7A, +0x633 => 0x73, +0x634 => 0x73, +0x635 => 0x73, +0x636 => 0x64, +0x637 => 0x74, +0x638 => 0x74, +0x639 => 0x61, +0x63A => 0x67, +0x641 => 0x66, +0x642 => 0x6B, +0x643 => 0x6B, +0x644 => 0x6C, +0x645 => 0x6D, +0x646 => 0x6E, +0x647 => 0x68, +0x648 => 0x6F, +0x64A => 0x79, +0x664 => 0x34, +0x665 => 0x35, +0x666 => 0x36, +0x67E => 0x70, +0x686 => 0x63, +0x698 => 0x7A, +0x6A9 => 0x6B, +0x6AF => 0x67, +0x6CC => 0x69, +0x6F0 => 0x30, +0x6F1 => 0x31, +0x6F2 => 0x32, +0x6F3 => 0x33, +0x6F4 => 0x34, +0x6F5 => 0x35, +0x6F6 => 0x36, +0x6F7 => 0x37, +0x6F8 => 0x38, +0x6F9 => 0x39, +0x905 => 0x61, +0x906 => 0x61, +0x907 => 0x69, +0x908 => 0x69, +0x909 => 0x75, +0x90A => 0x75, +0x90D => 0x65, +0x90F => 0x65, +0x910 => 0x61, +0x911 => 0x6F, +0x912 => 0x6F, +0x913 => 0x6F, +0x92C => 0x42, +0x932 => 0x4C, +0x1000 => 0x6B, +0x1002 => 0x67, +0x1005 => 0x73, +0x1007 => 0x7A, +0x1009 => 0x75, +0x100A => 0x69, +0x100B => 0x74, +0x100D => 0x64, +0x1010 => 0x74, +0x1012 => 0x64, +0x1014 => 0x6E, +0x1015 => 0x70, +0x1017 => 0x62, +0x1019 => 0x6D, +0x101A => 0x79, +0x101C => 0x6C, +0x101D => 0x77, +0x101F => 0x68, +0x1021 => 0x61, +0x1023 => 0x69, +0x1027 => 0x65, +0x102B => 0x61, +0x102C => 0x61, +0x102D => 0x6F, +0x102E => 0x69, +0x102F => 0x75, +0x1030 => 0x75, +0x1031 => 0x65, +0x1032 => 0x65, +0x103D => 0x77, +0x103E => 0x68, +0x10D0 => 0x61, +0x10D1 => 0x62, +0x10D2 => 0x67, +0x10D3 => 0x64, +0x10D4 => 0x65, +0x10D5 => 0x76, +0x10D6 => 0x7A, +0x10D7 => 0x74, +0x10D8 => 0x69, +0x10D9 => 0x6B, +0x10DA => 0x6C, +0x10DB => 0x6D, +0x10DC => 0x6E, +0x10DD => 0x6F, +0x10DE => 0x70, +0x10DF => 0x7A, +0x10E0 => 0x72, +0x10E1 => 0x73, +0x10E2 => 0x74, +0x10E3 => 0x75, +0x10E4 => 0x66, +0x10E5 => 0x6B, +0x10E6 => 0x67, +0x10E7 => 0x71, +0x10E8 => 0x73, +0x10E9 => 0x63, +0x10EA => 0x74, +0x10EB => 0x64, +0x10EC => 0x74, +0x10ED => 0x63, +0x10EE => 0x6B, +0x10EF => 0x6A, +0x10F0 => 0x68, +0x1D05 => 0x44, +0x1D06 => 0x44, +0x1D6D => 0x64, +0x1D81 => 0x64, +0x1D91 => 0x64, +0x1E9E => 0x53, +0x1EA0 => 0x41, +0x1EA1 => 0x61, +0x1EA2 => 0x41, +0x1EA3 => 0x61, +0x1EA4 => 0x41, +0x1EA5 => 0x61, +0x1EA6 => 0x41, +0x1EA7 => 0x61, +0x1EA8 => 0x41, +0x1EA9 => 0x61, +0x1EAA => 0x41, +0x1EAB => 0x61, +0x1EAC => 0x41, +0x1EAD => 0x61, +0x1EAE => 0x41, +0x1EAF => 0x61, +0x1EB0 => 0x41, +0x1EB1 => 0x61, +0x1EB2 => 0x41, +0x1EB3 => 0x61, +0x1EB4 => 0x41, +0x1EB5 => 0x61, +0x1EB6 => 0x41, +0x1EB7 => 0x61, +0x1EB8 => 0x45, +0x1EB9 => 0x65, +0x1EBA => 0x45, +0x1EBB => 0x65, +0x1EBC => 0x45, +0x1EBD => 0x65, +0x1EBE => 0x45, +0x1EBF => 0x65, +0x1EC0 => 0x45, +0x1EC1 => 0x65, +0x1EC2 => 0x45, +0x1EC3 => 0x65, +0x1EC4 => 0x45, +0x1EC5 => 0x65, +0x1EC6 => 0x45, +0x1EC7 => 0x65, +0x1EC8 => 0x49, +0x1EC9 => 0x69, +0x1ECA => 0x49, +0x1ECB => 0x69, +0x1ECC => 0x4F, +0x1ECD => 0x6F, +0x1ECE => 0x4F, +0x1ECF => 0x6F, +0x1ED0 => 0x4F, +0x1ED1 => 0x6F, +0x1ED2 => 0x4F, +0x1ED3 => 0x6F, +0x1ED4 => 0x4F, +0x1ED5 => 0x6F, +0x1ED6 => 0x4F, +0x1ED7 => 0x6F, +0x1ED8 => 0x4F, +0x1ED9 => 0x6F, +0x1EDA => 0x4F, +0x1EDB => 0x6F, +0x1EDC => 0x4F, +0x1EDD => 0x6F, +0x1EDE => 0x4F, +0x1EDF => 0x6F, +0x1EE0 => 0x4F, +0x1EE1 => 0x6F, +0x1EE2 => 0x4F, +0x1EE3 => 0x6F, +0x1EE4 => 0x55, +0x1EE5 => 0x75, +0x1EE6 => 0x55, +0x1EE7 => 0x75, +0x1EE8 => 0x55, +0x1EE9 => 0x75, +0x1EEA => 0x55, +0x1EEB => 0x75, +0x1EEC => 0x55, +0x1EED => 0x75, +0x1EEE => 0x55, +0x1EEF => 0x75, +0x1EF0 => 0x55, +0x1EF1 => 0x75, +0x1EF2 => 0x59, +0x1EF3 => 0x79, +0x1EF4 => 0x59, +0x1EF5 => 0x79, +0x1EF6 => 0x59, +0x1EF7 => 0x79, +0x1EF8 => 0x59, +0x1EF9 => 0x79, +0x1F00 => 0x61, +0x1F01 => 0x61, +0x1F02 => 0x61, +0x1F03 => 0x61, +0x1F04 => 0x61, +0x1F05 => 0x61, +0x1F06 => 0x61, +0x1F07 => 0x61, +0x1F08 => 0x41, +0x1F09 => 0x41, +0x1F0A => 0x41, +0x1F0B => 0x41, +0x1F0C => 0x41, +0x1F0D => 0x41, +0x1F0E => 0x41, +0x1F0F => 0x41, +0x1F10 => 0x65, +0x1F11 => 0x65, +0x1F12 => 0x65, +0x1F13 => 0x65, +0x1F14 => 0x65, +0x1F15 => 0x65, +0x1F18 => 0x45, +0x1F19 => 0x45, +0x1F1A => 0x45, +0x1F1B => 0x45, +0x1F1C => 0x45, +0x1F1D => 0x45, +0x1F30 => 0x69, +0x1F31 => 0x69, +0x1F32 => 0x69, +0x1F33 => 0x69, +0x1F34 => 0x69, +0x1F35 => 0x69, +0x1F36 => 0x69, +0x1F37 => 0x69, +0x1F38 => 0x49, +0x1F39 => 0x49, +0x1F3B => 0x49, +0x1F3C => 0x49, +0x1F3D => 0x49, +0x1F3E => 0x49, +0x1F3F => 0x49, +0x1F40 => 0x6F, +0x1F41 => 0x6F, +0x1F42 => 0x6F, +0x1F43 => 0x6F, +0x1F44 => 0x6F, +0x1F45 => 0x6F, +0x1F48 => 0x4F, +0x1F49 => 0x4F, +0x1F4A => 0x4F, +0x1F4B => 0x4F, +0x1F4C => 0x4F, +0x1F4D => 0x4F, +0x1F70 => 0x61, +0x1F72 => 0x65, +0x1F76 => 0x69, +0x1F78 => 0x6F, +0x1F80 => 0x61, +0x1F81 => 0x61, +0x1F82 => 0x61, +0x1F83 => 0x61, +0x1F84 => 0x61, +0x1F85 => 0x61, +0x1F86 => 0x61, +0x1F87 => 0x61, +0x1F88 => 0x41, +0x1F89 => 0x41, +0x1F8A => 0x41, +0x1F8B => 0x41, +0x1F8C => 0x41, +0x1F8D => 0x41, +0x1F8E => 0x41, +0x1F8F => 0x41, +0x1FB0 => 0x61, +0x1FB1 => 0x61, +0x1FB2 => 0x61, +0x1FB3 => 0x61, +0x1FB4 => 0x61, +0x1FB6 => 0x61, +0x1FB7 => 0x61, +0x1FB8 => 0x41, +0x1FB9 => 0x41, +0x1FBA => 0x41, +0x1FBC => 0x41, +0x1FC8 => 0x45, +0x1FD0 => 0x69, +0x1FD1 => 0x69, +0x1FD2 => 0x69, +0x1FD6 => 0x69, +0x1FD7 => 0x69, +0x1FD8 => 0x49, +0x1FD9 => 0x49, +0x1FDA => 0x49, +0x1FE8 => 0x59, +0x1FE9 => 0x59, +0x1FEA => 0x59, +0x1FF8 => 0x4F, +0x2000 => 0x20, +0x2001 => 0x20, +0x2002 => 0x20, +0x2003 => 0x20, +0x2004 => 0x20, +0x2005 => 0x20, +0x2006 => 0x20, +0x2007 => 0x20, +0x2008 => 0x20, +0x2009 => 0x20, +0x200A => 0x20, +0x202F => 0x20, +0x205F => 0x20, +0x2074 => 0x34, +0x2075 => 0x35, +0x2076 => 0x36, +0x2077 => 0x37, +0x2078 => 0x38, +0x2079 => 0x39, +0x2080 => 0x30, +0x2081 => 0x31, +0x2082 => 0x32, +0x2083 => 0x33, +0x2084 => 0x34, +0x2085 => 0x35, +0x2086 => 0x36, +0x2087 => 0x37, +0x2088 => 0x38, +0x2089 => 0x39, +0x3000 => 0x20, +]; diff --git a/vendor/opis/string/res/fold.php b/vendor/opis/string/res/fold.php new file mode 100644 index 000000000..e93f98191 --- /dev/null +++ b/vendor/opis/string/res/fold.php @@ -0,0 +1,1417 @@ + 0x61, +0x42 => 0x62, +0x43 => 0x63, +0x44 => 0x64, +0x45 => 0x65, +0x46 => 0x66, +0x47 => 0x67, +0x48 => 0x68, +0x49 => 0x69, +0x4A => 0x6A, +0x4B => 0x6B, +0x4C => 0x6C, +0x4D => 0x6D, +0x4E => 0x6E, +0x4F => 0x6F, +0x50 => 0x70, +0x51 => 0x71, +0x52 => 0x72, +0x53 => 0x73, +0x54 => 0x74, +0x55 => 0x75, +0x56 => 0x76, +0x57 => 0x77, +0x58 => 0x78, +0x59 => 0x79, +0x5A => 0x7A, +0xB5 => 0x3BC, +0xC0 => 0xE0, +0xC1 => 0xE1, +0xC2 => 0xE2, +0xC3 => 0xE3, +0xC4 => 0xE4, +0xC5 => 0xE5, +0xC6 => 0xE6, +0xC7 => 0xE7, +0xC8 => 0xE8, +0xC9 => 0xE9, +0xCA => 0xEA, +0xCB => 0xEB, +0xCC => 0xEC, +0xCD => 0xED, +0xCE => 0xEE, +0xCF => 0xEF, +0xD0 => 0xF0, +0xD1 => 0xF1, +0xD2 => 0xF2, +0xD3 => 0xF3, +0xD4 => 0xF4, +0xD5 => 0xF5, +0xD6 => 0xF6, +0xD8 => 0xF8, +0xD9 => 0xF9, +0xDA => 0xFA, +0xDB => 0xFB, +0xDC => 0xFC, +0xDD => 0xFD, +0xDE => 0xFE, +0x100 => 0x101, +0x102 => 0x103, +0x104 => 0x105, +0x106 => 0x107, +0x108 => 0x109, +0x10A => 0x10B, +0x10C => 0x10D, +0x10E => 0x10F, +0x110 => 0x111, +0x112 => 0x113, +0x114 => 0x115, +0x116 => 0x117, +0x118 => 0x119, +0x11A => 0x11B, +0x11C => 0x11D, +0x11E => 0x11F, +0x120 => 0x121, +0x122 => 0x123, +0x124 => 0x125, +0x126 => 0x127, +0x128 => 0x129, +0x12A => 0x12B, +0x12C => 0x12D, +0x12E => 0x12F, +0x132 => 0x133, +0x134 => 0x135, +0x136 => 0x137, +0x139 => 0x13A, +0x13B => 0x13C, +0x13D => 0x13E, +0x13F => 0x140, +0x141 => 0x142, +0x143 => 0x144, +0x145 => 0x146, +0x147 => 0x148, +0x14A => 0x14B, +0x14C => 0x14D, +0x14E => 0x14F, +0x150 => 0x151, +0x152 => 0x153, +0x154 => 0x155, +0x156 => 0x157, +0x158 => 0x159, +0x15A => 0x15B, +0x15C => 0x15D, +0x15E => 0x15F, +0x160 => 0x161, +0x162 => 0x163, +0x164 => 0x165, +0x166 => 0x167, +0x168 => 0x169, +0x16A => 0x16B, +0x16C => 0x16D, +0x16E => 0x16F, +0x170 => 0x171, +0x172 => 0x173, +0x174 => 0x175, +0x176 => 0x177, +0x178 => 0xFF, +0x179 => 0x17A, +0x17B => 0x17C, +0x17D => 0x17E, +0x17F => 0x73, +0x181 => 0x253, +0x182 => 0x183, +0x184 => 0x185, +0x186 => 0x254, +0x187 => 0x188, +0x189 => 0x256, +0x18A => 0x257, +0x18B => 0x18C, +0x18E => 0x1DD, +0x18F => 0x259, +0x190 => 0x25B, +0x191 => 0x192, +0x193 => 0x260, +0x194 => 0x263, +0x196 => 0x269, +0x197 => 0x268, +0x198 => 0x199, +0x19C => 0x26F, +0x19D => 0x272, +0x19F => 0x275, +0x1A0 => 0x1A1, +0x1A2 => 0x1A3, +0x1A4 => 0x1A5, +0x1A6 => 0x280, +0x1A7 => 0x1A8, +0x1A9 => 0x283, +0x1AC => 0x1AD, +0x1AE => 0x288, +0x1AF => 0x1B0, +0x1B1 => 0x28A, +0x1B2 => 0x28B, +0x1B3 => 0x1B4, +0x1B5 => 0x1B6, +0x1B7 => 0x292, +0x1B8 => 0x1B9, +0x1BC => 0x1BD, +0x1C4 => 0x1C6, +0x1C5 => 0x1C6, +0x1C7 => 0x1C9, +0x1C8 => 0x1C9, +0x1CA => 0x1CC, +0x1CB => 0x1CC, +0x1CD => 0x1CE, +0x1CF => 0x1D0, +0x1D1 => 0x1D2, +0x1D3 => 0x1D4, +0x1D5 => 0x1D6, +0x1D7 => 0x1D8, +0x1D9 => 0x1DA, +0x1DB => 0x1DC, +0x1DE => 0x1DF, +0x1E0 => 0x1E1, +0x1E2 => 0x1E3, +0x1E4 => 0x1E5, +0x1E6 => 0x1E7, +0x1E8 => 0x1E9, +0x1EA => 0x1EB, +0x1EC => 0x1ED, +0x1EE => 0x1EF, +0x1F1 => 0x1F3, +0x1F2 => 0x1F3, +0x1F4 => 0x1F5, +0x1F6 => 0x195, +0x1F7 => 0x1BF, +0x1F8 => 0x1F9, +0x1FA => 0x1FB, +0x1FC => 0x1FD, +0x1FE => 0x1FF, +0x200 => 0x201, +0x202 => 0x203, +0x204 => 0x205, +0x206 => 0x207, +0x208 => 0x209, +0x20A => 0x20B, +0x20C => 0x20D, +0x20E => 0x20F, +0x210 => 0x211, +0x212 => 0x213, +0x214 => 0x215, +0x216 => 0x217, +0x218 => 0x219, +0x21A => 0x21B, +0x21C => 0x21D, +0x21E => 0x21F, +0x220 => 0x19E, +0x222 => 0x223, +0x224 => 0x225, +0x226 => 0x227, +0x228 => 0x229, +0x22A => 0x22B, +0x22C => 0x22D, +0x22E => 0x22F, +0x230 => 0x231, +0x232 => 0x233, +0x23A => 0x2C65, +0x23B => 0x23C, +0x23D => 0x19A, +0x23E => 0x2C66, +0x241 => 0x242, +0x243 => 0x180, +0x244 => 0x289, +0x245 => 0x28C, +0x246 => 0x247, +0x248 => 0x249, +0x24A => 0x24B, +0x24C => 0x24D, +0x24E => 0x24F, +0x345 => 0x3B9, +0x370 => 0x371, +0x372 => 0x373, +0x376 => 0x377, +0x37F => 0x3F3, +0x386 => 0x3AC, +0x388 => 0x3AD, +0x389 => 0x3AE, +0x38A => 0x3AF, +0x38C => 0x3CC, +0x38E => 0x3CD, +0x38F => 0x3CE, +0x391 => 0x3B1, +0x392 => 0x3B2, +0x393 => 0x3B3, +0x394 => 0x3B4, +0x395 => 0x3B5, +0x396 => 0x3B6, +0x397 => 0x3B7, +0x398 => 0x3B8, +0x399 => 0x3B9, +0x39A => 0x3BA, +0x39B => 0x3BB, +0x39C => 0x3BC, +0x39D => 0x3BD, +0x39E => 0x3BE, +0x39F => 0x3BF, +0x3A0 => 0x3C0, +0x3A1 => 0x3C1, +0x3A3 => 0x3C3, +0x3A4 => 0x3C4, +0x3A5 => 0x3C5, +0x3A6 => 0x3C6, +0x3A7 => 0x3C7, +0x3A8 => 0x3C8, +0x3A9 => 0x3C9, +0x3AA => 0x3CA, +0x3AB => 0x3CB, +0x3C2 => 0x3C3, +0x3CF => 0x3D7, +0x3D0 => 0x3B2, +0x3D1 => 0x3B8, +0x3D5 => 0x3C6, +0x3D6 => 0x3C0, +0x3D8 => 0x3D9, +0x3DA => 0x3DB, +0x3DC => 0x3DD, +0x3DE => 0x3DF, +0x3E0 => 0x3E1, +0x3E2 => 0x3E3, +0x3E4 => 0x3E5, +0x3E6 => 0x3E7, +0x3E8 => 0x3E9, +0x3EA => 0x3EB, +0x3EC => 0x3ED, +0x3EE => 0x3EF, +0x3F0 => 0x3BA, +0x3F1 => 0x3C1, +0x3F4 => 0x3B8, +0x3F5 => 0x3B5, +0x3F7 => 0x3F8, +0x3F9 => 0x3F2, +0x3FA => 0x3FB, +0x3FD => 0x37B, +0x3FE => 0x37C, +0x3FF => 0x37D, +0x400 => 0x450, +0x401 => 0x451, +0x402 => 0x452, +0x403 => 0x453, +0x404 => 0x454, +0x405 => 0x455, +0x406 => 0x456, +0x407 => 0x457, +0x408 => 0x458, +0x409 => 0x459, +0x40A => 0x45A, +0x40B => 0x45B, +0x40C => 0x45C, +0x40D => 0x45D, +0x40E => 0x45E, +0x40F => 0x45F, +0x410 => 0x430, +0x411 => 0x431, +0x412 => 0x432, +0x413 => 0x433, +0x414 => 0x434, +0x415 => 0x435, +0x416 => 0x436, +0x417 => 0x437, +0x418 => 0x438, +0x419 => 0x439, +0x41A => 0x43A, +0x41B => 0x43B, +0x41C => 0x43C, +0x41D => 0x43D, +0x41E => 0x43E, +0x41F => 0x43F, +0x420 => 0x440, +0x421 => 0x441, +0x422 => 0x442, +0x423 => 0x443, +0x424 => 0x444, +0x425 => 0x445, +0x426 => 0x446, +0x427 => 0x447, +0x428 => 0x448, +0x429 => 0x449, +0x42A => 0x44A, +0x42B => 0x44B, +0x42C => 0x44C, +0x42D => 0x44D, +0x42E => 0x44E, +0x42F => 0x44F, +0x460 => 0x461, +0x462 => 0x463, +0x464 => 0x465, +0x466 => 0x467, +0x468 => 0x469, +0x46A => 0x46B, +0x46C => 0x46D, +0x46E => 0x46F, +0x470 => 0x471, +0x472 => 0x473, +0x474 => 0x475, +0x476 => 0x477, +0x478 => 0x479, +0x47A => 0x47B, +0x47C => 0x47D, +0x47E => 0x47F, +0x480 => 0x481, +0x48A => 0x48B, +0x48C => 0x48D, +0x48E => 0x48F, +0x490 => 0x491, +0x492 => 0x493, +0x494 => 0x495, +0x496 => 0x497, +0x498 => 0x499, +0x49A => 0x49B, +0x49C => 0x49D, +0x49E => 0x49F, +0x4A0 => 0x4A1, +0x4A2 => 0x4A3, +0x4A4 => 0x4A5, +0x4A6 => 0x4A7, +0x4A8 => 0x4A9, +0x4AA => 0x4AB, +0x4AC => 0x4AD, +0x4AE => 0x4AF, +0x4B0 => 0x4B1, +0x4B2 => 0x4B3, +0x4B4 => 0x4B5, +0x4B6 => 0x4B7, +0x4B8 => 0x4B9, +0x4BA => 0x4BB, +0x4BC => 0x4BD, +0x4BE => 0x4BF, +0x4C0 => 0x4CF, +0x4C1 => 0x4C2, +0x4C3 => 0x4C4, +0x4C5 => 0x4C6, +0x4C7 => 0x4C8, +0x4C9 => 0x4CA, +0x4CB => 0x4CC, +0x4CD => 0x4CE, +0x4D0 => 0x4D1, +0x4D2 => 0x4D3, +0x4D4 => 0x4D5, +0x4D6 => 0x4D7, +0x4D8 => 0x4D9, +0x4DA => 0x4DB, +0x4DC => 0x4DD, +0x4DE => 0x4DF, +0x4E0 => 0x4E1, +0x4E2 => 0x4E3, +0x4E4 => 0x4E5, +0x4E6 => 0x4E7, +0x4E8 => 0x4E9, +0x4EA => 0x4EB, +0x4EC => 0x4ED, +0x4EE => 0x4EF, +0x4F0 => 0x4F1, +0x4F2 => 0x4F3, +0x4F4 => 0x4F5, +0x4F6 => 0x4F7, +0x4F8 => 0x4F9, +0x4FA => 0x4FB, +0x4FC => 0x4FD, +0x4FE => 0x4FF, +0x500 => 0x501, +0x502 => 0x503, +0x504 => 0x505, +0x506 => 0x507, +0x508 => 0x509, +0x50A => 0x50B, +0x50C => 0x50D, +0x50E => 0x50F, +0x510 => 0x511, +0x512 => 0x513, +0x514 => 0x515, +0x516 => 0x517, +0x518 => 0x519, +0x51A => 0x51B, +0x51C => 0x51D, +0x51E => 0x51F, +0x520 => 0x521, +0x522 => 0x523, +0x524 => 0x525, +0x526 => 0x527, +0x528 => 0x529, +0x52A => 0x52B, +0x52C => 0x52D, +0x52E => 0x52F, +0x531 => 0x561, +0x532 => 0x562, +0x533 => 0x563, +0x534 => 0x564, +0x535 => 0x565, +0x536 => 0x566, +0x537 => 0x567, +0x538 => 0x568, +0x539 => 0x569, +0x53A => 0x56A, +0x53B => 0x56B, +0x53C => 0x56C, +0x53D => 0x56D, +0x53E => 0x56E, +0x53F => 0x56F, +0x540 => 0x570, +0x541 => 0x571, +0x542 => 0x572, +0x543 => 0x573, +0x544 => 0x574, +0x545 => 0x575, +0x546 => 0x576, +0x547 => 0x577, +0x548 => 0x578, +0x549 => 0x579, +0x54A => 0x57A, +0x54B => 0x57B, +0x54C => 0x57C, +0x54D => 0x57D, +0x54E => 0x57E, +0x54F => 0x57F, +0x550 => 0x580, +0x551 => 0x581, +0x552 => 0x582, +0x553 => 0x583, +0x554 => 0x584, +0x555 => 0x585, +0x556 => 0x586, +0x10A0 => 0x2D00, +0x10A1 => 0x2D01, +0x10A2 => 0x2D02, +0x10A3 => 0x2D03, +0x10A4 => 0x2D04, +0x10A5 => 0x2D05, +0x10A6 => 0x2D06, +0x10A7 => 0x2D07, +0x10A8 => 0x2D08, +0x10A9 => 0x2D09, +0x10AA => 0x2D0A, +0x10AB => 0x2D0B, +0x10AC => 0x2D0C, +0x10AD => 0x2D0D, +0x10AE => 0x2D0E, +0x10AF => 0x2D0F, +0x10B0 => 0x2D10, +0x10B1 => 0x2D11, +0x10B2 => 0x2D12, +0x10B3 => 0x2D13, +0x10B4 => 0x2D14, +0x10B5 => 0x2D15, +0x10B6 => 0x2D16, +0x10B7 => 0x2D17, +0x10B8 => 0x2D18, +0x10B9 => 0x2D19, +0x10BA => 0x2D1A, +0x10BB => 0x2D1B, +0x10BC => 0x2D1C, +0x10BD => 0x2D1D, +0x10BE => 0x2D1E, +0x10BF => 0x2D1F, +0x10C0 => 0x2D20, +0x10C1 => 0x2D21, +0x10C2 => 0x2D22, +0x10C3 => 0x2D23, +0x10C4 => 0x2D24, +0x10C5 => 0x2D25, +0x10C7 => 0x2D27, +0x10CD => 0x2D2D, +0x13F8 => 0x13F0, +0x13F9 => 0x13F1, +0x13FA => 0x13F2, +0x13FB => 0x13F3, +0x13FC => 0x13F4, +0x13FD => 0x13F5, +0x1C80 => 0x432, +0x1C81 => 0x434, +0x1C82 => 0x43E, +0x1C83 => 0x441, +0x1C84 => 0x442, +0x1C85 => 0x442, +0x1C86 => 0x44A, +0x1C87 => 0x463, +0x1C88 => 0xA64B, +0x1C90 => 0x10D0, +0x1C91 => 0x10D1, +0x1C92 => 0x10D2, +0x1C93 => 0x10D3, +0x1C94 => 0x10D4, +0x1C95 => 0x10D5, +0x1C96 => 0x10D6, +0x1C97 => 0x10D7, +0x1C98 => 0x10D8, +0x1C99 => 0x10D9, +0x1C9A => 0x10DA, +0x1C9B => 0x10DB, +0x1C9C => 0x10DC, +0x1C9D => 0x10DD, +0x1C9E => 0x10DE, +0x1C9F => 0x10DF, +0x1CA0 => 0x10E0, +0x1CA1 => 0x10E1, +0x1CA2 => 0x10E2, +0x1CA3 => 0x10E3, +0x1CA4 => 0x10E4, +0x1CA5 => 0x10E5, +0x1CA6 => 0x10E6, +0x1CA7 => 0x10E7, +0x1CA8 => 0x10E8, +0x1CA9 => 0x10E9, +0x1CAA => 0x10EA, +0x1CAB => 0x10EB, +0x1CAC => 0x10EC, +0x1CAD => 0x10ED, +0x1CAE => 0x10EE, +0x1CAF => 0x10EF, +0x1CB0 => 0x10F0, +0x1CB1 => 0x10F1, +0x1CB2 => 0x10F2, +0x1CB3 => 0x10F3, +0x1CB4 => 0x10F4, +0x1CB5 => 0x10F5, +0x1CB6 => 0x10F6, +0x1CB7 => 0x10F7, +0x1CB8 => 0x10F8, +0x1CB9 => 0x10F9, +0x1CBA => 0x10FA, +0x1CBD => 0x10FD, +0x1CBE => 0x10FE, +0x1CBF => 0x10FF, +0x1E00 => 0x1E01, +0x1E02 => 0x1E03, +0x1E04 => 0x1E05, +0x1E06 => 0x1E07, +0x1E08 => 0x1E09, +0x1E0A => 0x1E0B, +0x1E0C => 0x1E0D, +0x1E0E => 0x1E0F, +0x1E10 => 0x1E11, +0x1E12 => 0x1E13, +0x1E14 => 0x1E15, +0x1E16 => 0x1E17, +0x1E18 => 0x1E19, +0x1E1A => 0x1E1B, +0x1E1C => 0x1E1D, +0x1E1E => 0x1E1F, +0x1E20 => 0x1E21, +0x1E22 => 0x1E23, +0x1E24 => 0x1E25, +0x1E26 => 0x1E27, +0x1E28 => 0x1E29, +0x1E2A => 0x1E2B, +0x1E2C => 0x1E2D, +0x1E2E => 0x1E2F, +0x1E30 => 0x1E31, +0x1E32 => 0x1E33, +0x1E34 => 0x1E35, +0x1E36 => 0x1E37, +0x1E38 => 0x1E39, +0x1E3A => 0x1E3B, +0x1E3C => 0x1E3D, +0x1E3E => 0x1E3F, +0x1E40 => 0x1E41, +0x1E42 => 0x1E43, +0x1E44 => 0x1E45, +0x1E46 => 0x1E47, +0x1E48 => 0x1E49, +0x1E4A => 0x1E4B, +0x1E4C => 0x1E4D, +0x1E4E => 0x1E4F, +0x1E50 => 0x1E51, +0x1E52 => 0x1E53, +0x1E54 => 0x1E55, +0x1E56 => 0x1E57, +0x1E58 => 0x1E59, +0x1E5A => 0x1E5B, +0x1E5C => 0x1E5D, +0x1E5E => 0x1E5F, +0x1E60 => 0x1E61, +0x1E62 => 0x1E63, +0x1E64 => 0x1E65, +0x1E66 => 0x1E67, +0x1E68 => 0x1E69, +0x1E6A => 0x1E6B, +0x1E6C => 0x1E6D, +0x1E6E => 0x1E6F, +0x1E70 => 0x1E71, +0x1E72 => 0x1E73, +0x1E74 => 0x1E75, +0x1E76 => 0x1E77, +0x1E78 => 0x1E79, +0x1E7A => 0x1E7B, +0x1E7C => 0x1E7D, +0x1E7E => 0x1E7F, +0x1E80 => 0x1E81, +0x1E82 => 0x1E83, +0x1E84 => 0x1E85, +0x1E86 => 0x1E87, +0x1E88 => 0x1E89, +0x1E8A => 0x1E8B, +0x1E8C => 0x1E8D, +0x1E8E => 0x1E8F, +0x1E90 => 0x1E91, +0x1E92 => 0x1E93, +0x1E94 => 0x1E95, +0x1E9B => 0x1E61, +0x1E9E => 0xDF, +0x1EA0 => 0x1EA1, +0x1EA2 => 0x1EA3, +0x1EA4 => 0x1EA5, +0x1EA6 => 0x1EA7, +0x1EA8 => 0x1EA9, +0x1EAA => 0x1EAB, +0x1EAC => 0x1EAD, +0x1EAE => 0x1EAF, +0x1EB0 => 0x1EB1, +0x1EB2 => 0x1EB3, +0x1EB4 => 0x1EB5, +0x1EB6 => 0x1EB7, +0x1EB8 => 0x1EB9, +0x1EBA => 0x1EBB, +0x1EBC => 0x1EBD, +0x1EBE => 0x1EBF, +0x1EC0 => 0x1EC1, +0x1EC2 => 0x1EC3, +0x1EC4 => 0x1EC5, +0x1EC6 => 0x1EC7, +0x1EC8 => 0x1EC9, +0x1ECA => 0x1ECB, +0x1ECC => 0x1ECD, +0x1ECE => 0x1ECF, +0x1ED0 => 0x1ED1, +0x1ED2 => 0x1ED3, +0x1ED4 => 0x1ED5, +0x1ED6 => 0x1ED7, +0x1ED8 => 0x1ED9, +0x1EDA => 0x1EDB, +0x1EDC => 0x1EDD, +0x1EDE => 0x1EDF, +0x1EE0 => 0x1EE1, +0x1EE2 => 0x1EE3, +0x1EE4 => 0x1EE5, +0x1EE6 => 0x1EE7, +0x1EE8 => 0x1EE9, +0x1EEA => 0x1EEB, +0x1EEC => 0x1EED, +0x1EEE => 0x1EEF, +0x1EF0 => 0x1EF1, +0x1EF2 => 0x1EF3, +0x1EF4 => 0x1EF5, +0x1EF6 => 0x1EF7, +0x1EF8 => 0x1EF9, +0x1EFA => 0x1EFB, +0x1EFC => 0x1EFD, +0x1EFE => 0x1EFF, +0x1F08 => 0x1F00, +0x1F09 => 0x1F01, +0x1F0A => 0x1F02, +0x1F0B => 0x1F03, +0x1F0C => 0x1F04, +0x1F0D => 0x1F05, +0x1F0E => 0x1F06, +0x1F0F => 0x1F07, +0x1F18 => 0x1F10, +0x1F19 => 0x1F11, +0x1F1A => 0x1F12, +0x1F1B => 0x1F13, +0x1F1C => 0x1F14, +0x1F1D => 0x1F15, +0x1F28 => 0x1F20, +0x1F29 => 0x1F21, +0x1F2A => 0x1F22, +0x1F2B => 0x1F23, +0x1F2C => 0x1F24, +0x1F2D => 0x1F25, +0x1F2E => 0x1F26, +0x1F2F => 0x1F27, +0x1F38 => 0x1F30, +0x1F39 => 0x1F31, +0x1F3A => 0x1F32, +0x1F3B => 0x1F33, +0x1F3C => 0x1F34, +0x1F3D => 0x1F35, +0x1F3E => 0x1F36, +0x1F3F => 0x1F37, +0x1F48 => 0x1F40, +0x1F49 => 0x1F41, +0x1F4A => 0x1F42, +0x1F4B => 0x1F43, +0x1F4C => 0x1F44, +0x1F4D => 0x1F45, +0x1F59 => 0x1F51, +0x1F5B => 0x1F53, +0x1F5D => 0x1F55, +0x1F5F => 0x1F57, +0x1F68 => 0x1F60, +0x1F69 => 0x1F61, +0x1F6A => 0x1F62, +0x1F6B => 0x1F63, +0x1F6C => 0x1F64, +0x1F6D => 0x1F65, +0x1F6E => 0x1F66, +0x1F6F => 0x1F67, +0x1F88 => 0x1F80, +0x1F89 => 0x1F81, +0x1F8A => 0x1F82, +0x1F8B => 0x1F83, +0x1F8C => 0x1F84, +0x1F8D => 0x1F85, +0x1F8E => 0x1F86, +0x1F8F => 0x1F87, +0x1F98 => 0x1F90, +0x1F99 => 0x1F91, +0x1F9A => 0x1F92, +0x1F9B => 0x1F93, +0x1F9C => 0x1F94, +0x1F9D => 0x1F95, +0x1F9E => 0x1F96, +0x1F9F => 0x1F97, +0x1FA8 => 0x1FA0, +0x1FA9 => 0x1FA1, +0x1FAA => 0x1FA2, +0x1FAB => 0x1FA3, +0x1FAC => 0x1FA4, +0x1FAD => 0x1FA5, +0x1FAE => 0x1FA6, +0x1FAF => 0x1FA7, +0x1FB8 => 0x1FB0, +0x1FB9 => 0x1FB1, +0x1FBA => 0x1F70, +0x1FBB => 0x1F71, +0x1FBC => 0x1FB3, +0x1FBE => 0x3B9, +0x1FC8 => 0x1F72, +0x1FC9 => 0x1F73, +0x1FCA => 0x1F74, +0x1FCB => 0x1F75, +0x1FCC => 0x1FC3, +0x1FD8 => 0x1FD0, +0x1FD9 => 0x1FD1, +0x1FDA => 0x1F76, +0x1FDB => 0x1F77, +0x1FE8 => 0x1FE0, +0x1FE9 => 0x1FE1, +0x1FEA => 0x1F7A, +0x1FEB => 0x1F7B, +0x1FEC => 0x1FE5, +0x1FF8 => 0x1F78, +0x1FF9 => 0x1F79, +0x1FFA => 0x1F7C, +0x1FFB => 0x1F7D, +0x1FFC => 0x1FF3, +0x2126 => 0x3C9, +0x212A => 0x6B, +0x212B => 0xE5, +0x2132 => 0x214E, +0x2160 => 0x2170, +0x2161 => 0x2171, +0x2162 => 0x2172, +0x2163 => 0x2173, +0x2164 => 0x2174, +0x2165 => 0x2175, +0x2166 => 0x2176, +0x2167 => 0x2177, +0x2168 => 0x2178, +0x2169 => 0x2179, +0x216A => 0x217A, +0x216B => 0x217B, +0x216C => 0x217C, +0x216D => 0x217D, +0x216E => 0x217E, +0x216F => 0x217F, +0x2183 => 0x2184, +0x24B6 => 0x24D0, +0x24B7 => 0x24D1, +0x24B8 => 0x24D2, +0x24B9 => 0x24D3, +0x24BA => 0x24D4, +0x24BB => 0x24D5, +0x24BC => 0x24D6, +0x24BD => 0x24D7, +0x24BE => 0x24D8, +0x24BF => 0x24D9, +0x24C0 => 0x24DA, +0x24C1 => 0x24DB, +0x24C2 => 0x24DC, +0x24C3 => 0x24DD, +0x24C4 => 0x24DE, +0x24C5 => 0x24DF, +0x24C6 => 0x24E0, +0x24C7 => 0x24E1, +0x24C8 => 0x24E2, +0x24C9 => 0x24E3, +0x24CA => 0x24E4, +0x24CB => 0x24E5, +0x24CC => 0x24E6, +0x24CD => 0x24E7, +0x24CE => 0x24E8, +0x24CF => 0x24E9, +0x2C00 => 0x2C30, +0x2C01 => 0x2C31, +0x2C02 => 0x2C32, +0x2C03 => 0x2C33, +0x2C04 => 0x2C34, +0x2C05 => 0x2C35, +0x2C06 => 0x2C36, +0x2C07 => 0x2C37, +0x2C08 => 0x2C38, +0x2C09 => 0x2C39, +0x2C0A => 0x2C3A, +0x2C0B => 0x2C3B, +0x2C0C => 0x2C3C, +0x2C0D => 0x2C3D, +0x2C0E => 0x2C3E, +0x2C0F => 0x2C3F, +0x2C10 => 0x2C40, +0x2C11 => 0x2C41, +0x2C12 => 0x2C42, +0x2C13 => 0x2C43, +0x2C14 => 0x2C44, +0x2C15 => 0x2C45, +0x2C16 => 0x2C46, +0x2C17 => 0x2C47, +0x2C18 => 0x2C48, +0x2C19 => 0x2C49, +0x2C1A => 0x2C4A, +0x2C1B => 0x2C4B, +0x2C1C => 0x2C4C, +0x2C1D => 0x2C4D, +0x2C1E => 0x2C4E, +0x2C1F => 0x2C4F, +0x2C20 => 0x2C50, +0x2C21 => 0x2C51, +0x2C22 => 0x2C52, +0x2C23 => 0x2C53, +0x2C24 => 0x2C54, +0x2C25 => 0x2C55, +0x2C26 => 0x2C56, +0x2C27 => 0x2C57, +0x2C28 => 0x2C58, +0x2C29 => 0x2C59, +0x2C2A => 0x2C5A, +0x2C2B => 0x2C5B, +0x2C2C => 0x2C5C, +0x2C2D => 0x2C5D, +0x2C2E => 0x2C5E, +0x2C60 => 0x2C61, +0x2C62 => 0x26B, +0x2C63 => 0x1D7D, +0x2C64 => 0x27D, +0x2C67 => 0x2C68, +0x2C69 => 0x2C6A, +0x2C6B => 0x2C6C, +0x2C6D => 0x251, +0x2C6E => 0x271, +0x2C6F => 0x250, +0x2C70 => 0x252, +0x2C72 => 0x2C73, +0x2C75 => 0x2C76, +0x2C7E => 0x23F, +0x2C7F => 0x240, +0x2C80 => 0x2C81, +0x2C82 => 0x2C83, +0x2C84 => 0x2C85, +0x2C86 => 0x2C87, +0x2C88 => 0x2C89, +0x2C8A => 0x2C8B, +0x2C8C => 0x2C8D, +0x2C8E => 0x2C8F, +0x2C90 => 0x2C91, +0x2C92 => 0x2C93, +0x2C94 => 0x2C95, +0x2C96 => 0x2C97, +0x2C98 => 0x2C99, +0x2C9A => 0x2C9B, +0x2C9C => 0x2C9D, +0x2C9E => 0x2C9F, +0x2CA0 => 0x2CA1, +0x2CA2 => 0x2CA3, +0x2CA4 => 0x2CA5, +0x2CA6 => 0x2CA7, +0x2CA8 => 0x2CA9, +0x2CAA => 0x2CAB, +0x2CAC => 0x2CAD, +0x2CAE => 0x2CAF, +0x2CB0 => 0x2CB1, +0x2CB2 => 0x2CB3, +0x2CB4 => 0x2CB5, +0x2CB6 => 0x2CB7, +0x2CB8 => 0x2CB9, +0x2CBA => 0x2CBB, +0x2CBC => 0x2CBD, +0x2CBE => 0x2CBF, +0x2CC0 => 0x2CC1, +0x2CC2 => 0x2CC3, +0x2CC4 => 0x2CC5, +0x2CC6 => 0x2CC7, +0x2CC8 => 0x2CC9, +0x2CCA => 0x2CCB, +0x2CCC => 0x2CCD, +0x2CCE => 0x2CCF, +0x2CD0 => 0x2CD1, +0x2CD2 => 0x2CD3, +0x2CD4 => 0x2CD5, +0x2CD6 => 0x2CD7, +0x2CD8 => 0x2CD9, +0x2CDA => 0x2CDB, +0x2CDC => 0x2CDD, +0x2CDE => 0x2CDF, +0x2CE0 => 0x2CE1, +0x2CE2 => 0x2CE3, +0x2CEB => 0x2CEC, +0x2CED => 0x2CEE, +0x2CF2 => 0x2CF3, +0xA640 => 0xA641, +0xA642 => 0xA643, +0xA644 => 0xA645, +0xA646 => 0xA647, +0xA648 => 0xA649, +0xA64A => 0xA64B, +0xA64C => 0xA64D, +0xA64E => 0xA64F, +0xA650 => 0xA651, +0xA652 => 0xA653, +0xA654 => 0xA655, +0xA656 => 0xA657, +0xA658 => 0xA659, +0xA65A => 0xA65B, +0xA65C => 0xA65D, +0xA65E => 0xA65F, +0xA660 => 0xA661, +0xA662 => 0xA663, +0xA664 => 0xA665, +0xA666 => 0xA667, +0xA668 => 0xA669, +0xA66A => 0xA66B, +0xA66C => 0xA66D, +0xA680 => 0xA681, +0xA682 => 0xA683, +0xA684 => 0xA685, +0xA686 => 0xA687, +0xA688 => 0xA689, +0xA68A => 0xA68B, +0xA68C => 0xA68D, +0xA68E => 0xA68F, +0xA690 => 0xA691, +0xA692 => 0xA693, +0xA694 => 0xA695, +0xA696 => 0xA697, +0xA698 => 0xA699, +0xA69A => 0xA69B, +0xA722 => 0xA723, +0xA724 => 0xA725, +0xA726 => 0xA727, +0xA728 => 0xA729, +0xA72A => 0xA72B, +0xA72C => 0xA72D, +0xA72E => 0xA72F, +0xA732 => 0xA733, +0xA734 => 0xA735, +0xA736 => 0xA737, +0xA738 => 0xA739, +0xA73A => 0xA73B, +0xA73C => 0xA73D, +0xA73E => 0xA73F, +0xA740 => 0xA741, +0xA742 => 0xA743, +0xA744 => 0xA745, +0xA746 => 0xA747, +0xA748 => 0xA749, +0xA74A => 0xA74B, +0xA74C => 0xA74D, +0xA74E => 0xA74F, +0xA750 => 0xA751, +0xA752 => 0xA753, +0xA754 => 0xA755, +0xA756 => 0xA757, +0xA758 => 0xA759, +0xA75A => 0xA75B, +0xA75C => 0xA75D, +0xA75E => 0xA75F, +0xA760 => 0xA761, +0xA762 => 0xA763, +0xA764 => 0xA765, +0xA766 => 0xA767, +0xA768 => 0xA769, +0xA76A => 0xA76B, +0xA76C => 0xA76D, +0xA76E => 0xA76F, +0xA779 => 0xA77A, +0xA77B => 0xA77C, +0xA77D => 0x1D79, +0xA77E => 0xA77F, +0xA780 => 0xA781, +0xA782 => 0xA783, +0xA784 => 0xA785, +0xA786 => 0xA787, +0xA78B => 0xA78C, +0xA78D => 0x265, +0xA790 => 0xA791, +0xA792 => 0xA793, +0xA796 => 0xA797, +0xA798 => 0xA799, +0xA79A => 0xA79B, +0xA79C => 0xA79D, +0xA79E => 0xA79F, +0xA7A0 => 0xA7A1, +0xA7A2 => 0xA7A3, +0xA7A4 => 0xA7A5, +0xA7A6 => 0xA7A7, +0xA7A8 => 0xA7A9, +0xA7AA => 0x266, +0xA7AB => 0x25C, +0xA7AC => 0x261, +0xA7AD => 0x26C, +0xA7AE => 0x26A, +0xA7B0 => 0x29E, +0xA7B1 => 0x287, +0xA7B2 => 0x29D, +0xA7B3 => 0xAB53, +0xA7B4 => 0xA7B5, +0xA7B6 => 0xA7B7, +0xA7B8 => 0xA7B9, +0xA7BA => 0xA7BB, +0xA7BC => 0xA7BD, +0xA7BE => 0xA7BF, +0xA7C2 => 0xA7C3, +0xA7C4 => 0xA794, +0xA7C5 => 0x282, +0xA7C6 => 0x1D8E, +0xA7C7 => 0xA7C8, +0xA7C9 => 0xA7CA, +0xA7F5 => 0xA7F6, +0xAB70 => 0x13A0, +0xAB71 => 0x13A1, +0xAB72 => 0x13A2, +0xAB73 => 0x13A3, +0xAB74 => 0x13A4, +0xAB75 => 0x13A5, +0xAB76 => 0x13A6, +0xAB77 => 0x13A7, +0xAB78 => 0x13A8, +0xAB79 => 0x13A9, +0xAB7A => 0x13AA, +0xAB7B => 0x13AB, +0xAB7C => 0x13AC, +0xAB7D => 0x13AD, +0xAB7E => 0x13AE, +0xAB7F => 0x13AF, +0xAB80 => 0x13B0, +0xAB81 => 0x13B1, +0xAB82 => 0x13B2, +0xAB83 => 0x13B3, +0xAB84 => 0x13B4, +0xAB85 => 0x13B5, +0xAB86 => 0x13B6, +0xAB87 => 0x13B7, +0xAB88 => 0x13B8, +0xAB89 => 0x13B9, +0xAB8A => 0x13BA, +0xAB8B => 0x13BB, +0xAB8C => 0x13BC, +0xAB8D => 0x13BD, +0xAB8E => 0x13BE, +0xAB8F => 0x13BF, +0xAB90 => 0x13C0, +0xAB91 => 0x13C1, +0xAB92 => 0x13C2, +0xAB93 => 0x13C3, +0xAB94 => 0x13C4, +0xAB95 => 0x13C5, +0xAB96 => 0x13C6, +0xAB97 => 0x13C7, +0xAB98 => 0x13C8, +0xAB99 => 0x13C9, +0xAB9A => 0x13CA, +0xAB9B => 0x13CB, +0xAB9C => 0x13CC, +0xAB9D => 0x13CD, +0xAB9E => 0x13CE, +0xAB9F => 0x13CF, +0xABA0 => 0x13D0, +0xABA1 => 0x13D1, +0xABA2 => 0x13D2, +0xABA3 => 0x13D3, +0xABA4 => 0x13D4, +0xABA5 => 0x13D5, +0xABA6 => 0x13D6, +0xABA7 => 0x13D7, +0xABA8 => 0x13D8, +0xABA9 => 0x13D9, +0xABAA => 0x13DA, +0xABAB => 0x13DB, +0xABAC => 0x13DC, +0xABAD => 0x13DD, +0xABAE => 0x13DE, +0xABAF => 0x13DF, +0xABB0 => 0x13E0, +0xABB1 => 0x13E1, +0xABB2 => 0x13E2, +0xABB3 => 0x13E3, +0xABB4 => 0x13E4, +0xABB5 => 0x13E5, +0xABB6 => 0x13E6, +0xABB7 => 0x13E7, +0xABB8 => 0x13E8, +0xABB9 => 0x13E9, +0xABBA => 0x13EA, +0xABBB => 0x13EB, +0xABBC => 0x13EC, +0xABBD => 0x13ED, +0xABBE => 0x13EE, +0xABBF => 0x13EF, +0xFF21 => 0xFF41, +0xFF22 => 0xFF42, +0xFF23 => 0xFF43, +0xFF24 => 0xFF44, +0xFF25 => 0xFF45, +0xFF26 => 0xFF46, +0xFF27 => 0xFF47, +0xFF28 => 0xFF48, +0xFF29 => 0xFF49, +0xFF2A => 0xFF4A, +0xFF2B => 0xFF4B, +0xFF2C => 0xFF4C, +0xFF2D => 0xFF4D, +0xFF2E => 0xFF4E, +0xFF2F => 0xFF4F, +0xFF30 => 0xFF50, +0xFF31 => 0xFF51, +0xFF32 => 0xFF52, +0xFF33 => 0xFF53, +0xFF34 => 0xFF54, +0xFF35 => 0xFF55, +0xFF36 => 0xFF56, +0xFF37 => 0xFF57, +0xFF38 => 0xFF58, +0xFF39 => 0xFF59, +0xFF3A => 0xFF5A, +0x10400 => 0x10428, +0x10401 => 0x10429, +0x10402 => 0x1042A, +0x10403 => 0x1042B, +0x10404 => 0x1042C, +0x10405 => 0x1042D, +0x10406 => 0x1042E, +0x10407 => 0x1042F, +0x10408 => 0x10430, +0x10409 => 0x10431, +0x1040A => 0x10432, +0x1040B => 0x10433, +0x1040C => 0x10434, +0x1040D => 0x10435, +0x1040E => 0x10436, +0x1040F => 0x10437, +0x10410 => 0x10438, +0x10411 => 0x10439, +0x10412 => 0x1043A, +0x10413 => 0x1043B, +0x10414 => 0x1043C, +0x10415 => 0x1043D, +0x10416 => 0x1043E, +0x10417 => 0x1043F, +0x10418 => 0x10440, +0x10419 => 0x10441, +0x1041A => 0x10442, +0x1041B => 0x10443, +0x1041C => 0x10444, +0x1041D => 0x10445, +0x1041E => 0x10446, +0x1041F => 0x10447, +0x10420 => 0x10448, +0x10421 => 0x10449, +0x10422 => 0x1044A, +0x10423 => 0x1044B, +0x10424 => 0x1044C, +0x10425 => 0x1044D, +0x10426 => 0x1044E, +0x10427 => 0x1044F, +0x104B0 => 0x104D8, +0x104B1 => 0x104D9, +0x104B2 => 0x104DA, +0x104B3 => 0x104DB, +0x104B4 => 0x104DC, +0x104B5 => 0x104DD, +0x104B6 => 0x104DE, +0x104B7 => 0x104DF, +0x104B8 => 0x104E0, +0x104B9 => 0x104E1, +0x104BA => 0x104E2, +0x104BB => 0x104E3, +0x104BC => 0x104E4, +0x104BD => 0x104E5, +0x104BE => 0x104E6, +0x104BF => 0x104E7, +0x104C0 => 0x104E8, +0x104C1 => 0x104E9, +0x104C2 => 0x104EA, +0x104C3 => 0x104EB, +0x104C4 => 0x104EC, +0x104C5 => 0x104ED, +0x104C6 => 0x104EE, +0x104C7 => 0x104EF, +0x104C8 => 0x104F0, +0x104C9 => 0x104F1, +0x104CA => 0x104F2, +0x104CB => 0x104F3, +0x104CC => 0x104F4, +0x104CD => 0x104F5, +0x104CE => 0x104F6, +0x104CF => 0x104F7, +0x104D0 => 0x104F8, +0x104D1 => 0x104F9, +0x104D2 => 0x104FA, +0x104D3 => 0x104FB, +0x10C80 => 0x10CC0, +0x10C81 => 0x10CC1, +0x10C82 => 0x10CC2, +0x10C83 => 0x10CC3, +0x10C84 => 0x10CC4, +0x10C85 => 0x10CC5, +0x10C86 => 0x10CC6, +0x10C87 => 0x10CC7, +0x10C88 => 0x10CC8, +0x10C89 => 0x10CC9, +0x10C8A => 0x10CCA, +0x10C8B => 0x10CCB, +0x10C8C => 0x10CCC, +0x10C8D => 0x10CCD, +0x10C8E => 0x10CCE, +0x10C8F => 0x10CCF, +0x10C90 => 0x10CD0, +0x10C91 => 0x10CD1, +0x10C92 => 0x10CD2, +0x10C93 => 0x10CD3, +0x10C94 => 0x10CD4, +0x10C95 => 0x10CD5, +0x10C96 => 0x10CD6, +0x10C97 => 0x10CD7, +0x10C98 => 0x10CD8, +0x10C99 => 0x10CD9, +0x10C9A => 0x10CDA, +0x10C9B => 0x10CDB, +0x10C9C => 0x10CDC, +0x10C9D => 0x10CDD, +0x10C9E => 0x10CDE, +0x10C9F => 0x10CDF, +0x10CA0 => 0x10CE0, +0x10CA1 => 0x10CE1, +0x10CA2 => 0x10CE2, +0x10CA3 => 0x10CE3, +0x10CA4 => 0x10CE4, +0x10CA5 => 0x10CE5, +0x10CA6 => 0x10CE6, +0x10CA7 => 0x10CE7, +0x10CA8 => 0x10CE8, +0x10CA9 => 0x10CE9, +0x10CAA => 0x10CEA, +0x10CAB => 0x10CEB, +0x10CAC => 0x10CEC, +0x10CAD => 0x10CED, +0x10CAE => 0x10CEE, +0x10CAF => 0x10CEF, +0x10CB0 => 0x10CF0, +0x10CB1 => 0x10CF1, +0x10CB2 => 0x10CF2, +0x118A0 => 0x118C0, +0x118A1 => 0x118C1, +0x118A2 => 0x118C2, +0x118A3 => 0x118C3, +0x118A4 => 0x118C4, +0x118A5 => 0x118C5, +0x118A6 => 0x118C6, +0x118A7 => 0x118C7, +0x118A8 => 0x118C8, +0x118A9 => 0x118C9, +0x118AA => 0x118CA, +0x118AB => 0x118CB, +0x118AC => 0x118CC, +0x118AD => 0x118CD, +0x118AE => 0x118CE, +0x118AF => 0x118CF, +0x118B0 => 0x118D0, +0x118B1 => 0x118D1, +0x118B2 => 0x118D2, +0x118B3 => 0x118D3, +0x118B4 => 0x118D4, +0x118B5 => 0x118D5, +0x118B6 => 0x118D6, +0x118B7 => 0x118D7, +0x118B8 => 0x118D8, +0x118B9 => 0x118D9, +0x118BA => 0x118DA, +0x118BB => 0x118DB, +0x118BC => 0x118DC, +0x118BD => 0x118DD, +0x118BE => 0x118DE, +0x118BF => 0x118DF, +0x16E40 => 0x16E60, +0x16E41 => 0x16E61, +0x16E42 => 0x16E62, +0x16E43 => 0x16E63, +0x16E44 => 0x16E64, +0x16E45 => 0x16E65, +0x16E46 => 0x16E66, +0x16E47 => 0x16E67, +0x16E48 => 0x16E68, +0x16E49 => 0x16E69, +0x16E4A => 0x16E6A, +0x16E4B => 0x16E6B, +0x16E4C => 0x16E6C, +0x16E4D => 0x16E6D, +0x16E4E => 0x16E6E, +0x16E4F => 0x16E6F, +0x16E50 => 0x16E70, +0x16E51 => 0x16E71, +0x16E52 => 0x16E72, +0x16E53 => 0x16E73, +0x16E54 => 0x16E74, +0x16E55 => 0x16E75, +0x16E56 => 0x16E76, +0x16E57 => 0x16E77, +0x16E58 => 0x16E78, +0x16E59 => 0x16E79, +0x16E5A => 0x16E7A, +0x16E5B => 0x16E7B, +0x16E5C => 0x16E7C, +0x16E5D => 0x16E7D, +0x16E5E => 0x16E7E, +0x16E5F => 0x16E7F, +0x1E900 => 0x1E922, +0x1E901 => 0x1E923, +0x1E902 => 0x1E924, +0x1E903 => 0x1E925, +0x1E904 => 0x1E926, +0x1E905 => 0x1E927, +0x1E906 => 0x1E928, +0x1E907 => 0x1E929, +0x1E908 => 0x1E92A, +0x1E909 => 0x1E92B, +0x1E90A => 0x1E92C, +0x1E90B => 0x1E92D, +0x1E90C => 0x1E92E, +0x1E90D => 0x1E92F, +0x1E90E => 0x1E930, +0x1E90F => 0x1E931, +0x1E910 => 0x1E932, +0x1E911 => 0x1E933, +0x1E912 => 0x1E934, +0x1E913 => 0x1E935, +0x1E914 => 0x1E936, +0x1E915 => 0x1E937, +0x1E916 => 0x1E938, +0x1E917 => 0x1E939, +0x1E918 => 0x1E93A, +0x1E919 => 0x1E93B, +0x1E91A => 0x1E93C, +0x1E91B => 0x1E93D, +0x1E91C => 0x1E93E, +0x1E91D => 0x1E93F, +0x1E91E => 0x1E940, +0x1E91F => 0x1E941, +0x1E920 => 0x1E942, +0x1E921 => 0x1E943, +]; diff --git a/vendor/opis/string/res/lower.php b/vendor/opis/string/res/lower.php new file mode 100644 index 000000000..b5697d1dc --- /dev/null +++ b/vendor/opis/string/res/lower.php @@ -0,0 +1,1396 @@ + 0x61, +0x42 => 0x62, +0x43 => 0x63, +0x44 => 0x64, +0x45 => 0x65, +0x46 => 0x66, +0x47 => 0x67, +0x48 => 0x68, +0x49 => 0x69, +0x4A => 0x6A, +0x4B => 0x6B, +0x4C => 0x6C, +0x4D => 0x6D, +0x4E => 0x6E, +0x4F => 0x6F, +0x50 => 0x70, +0x51 => 0x71, +0x52 => 0x72, +0x53 => 0x73, +0x54 => 0x74, +0x55 => 0x75, +0x56 => 0x76, +0x57 => 0x77, +0x58 => 0x78, +0x59 => 0x79, +0x5A => 0x7A, +0xC0 => 0xE0, +0xC1 => 0xE1, +0xC2 => 0xE2, +0xC3 => 0xE3, +0xC4 => 0xE4, +0xC5 => 0xE5, +0xC6 => 0xE6, +0xC7 => 0xE7, +0xC8 => 0xE8, +0xC9 => 0xE9, +0xCA => 0xEA, +0xCB => 0xEB, +0xCC => 0xEC, +0xCD => 0xED, +0xCE => 0xEE, +0xCF => 0xEF, +0xD0 => 0xF0, +0xD1 => 0xF1, +0xD2 => 0xF2, +0xD3 => 0xF3, +0xD4 => 0xF4, +0xD5 => 0xF5, +0xD6 => 0xF6, +0xD8 => 0xF8, +0xD9 => 0xF9, +0xDA => 0xFA, +0xDB => 0xFB, +0xDC => 0xFC, +0xDD => 0xFD, +0xDE => 0xFE, +0x100 => 0x101, +0x102 => 0x103, +0x104 => 0x105, +0x106 => 0x107, +0x108 => 0x109, +0x10A => 0x10B, +0x10C => 0x10D, +0x10E => 0x10F, +0x110 => 0x111, +0x112 => 0x113, +0x114 => 0x115, +0x116 => 0x117, +0x118 => 0x119, +0x11A => 0x11B, +0x11C => 0x11D, +0x11E => 0x11F, +0x120 => 0x121, +0x122 => 0x123, +0x124 => 0x125, +0x126 => 0x127, +0x128 => 0x129, +0x12A => 0x12B, +0x12C => 0x12D, +0x12E => 0x12F, +0x130 => 0x69, +0x132 => 0x133, +0x134 => 0x135, +0x136 => 0x137, +0x139 => 0x13A, +0x13B => 0x13C, +0x13D => 0x13E, +0x13F => 0x140, +0x141 => 0x142, +0x143 => 0x144, +0x145 => 0x146, +0x147 => 0x148, +0x14A => 0x14B, +0x14C => 0x14D, +0x14E => 0x14F, +0x150 => 0x151, +0x152 => 0x153, +0x154 => 0x155, +0x156 => 0x157, +0x158 => 0x159, +0x15A => 0x15B, +0x15C => 0x15D, +0x15E => 0x15F, +0x160 => 0x161, +0x162 => 0x163, +0x164 => 0x165, +0x166 => 0x167, +0x168 => 0x169, +0x16A => 0x16B, +0x16C => 0x16D, +0x16E => 0x16F, +0x170 => 0x171, +0x172 => 0x173, +0x174 => 0x175, +0x176 => 0x177, +0x178 => 0xFF, +0x179 => 0x17A, +0x17B => 0x17C, +0x17D => 0x17E, +0x181 => 0x253, +0x182 => 0x183, +0x184 => 0x185, +0x186 => 0x254, +0x187 => 0x188, +0x189 => 0x256, +0x18A => 0x257, +0x18B => 0x18C, +0x18E => 0x1DD, +0x18F => 0x259, +0x190 => 0x25B, +0x191 => 0x192, +0x193 => 0x260, +0x194 => 0x263, +0x196 => 0x269, +0x197 => 0x268, +0x198 => 0x199, +0x19C => 0x26F, +0x19D => 0x272, +0x19F => 0x275, +0x1A0 => 0x1A1, +0x1A2 => 0x1A3, +0x1A4 => 0x1A5, +0x1A6 => 0x280, +0x1A7 => 0x1A8, +0x1A9 => 0x283, +0x1AC => 0x1AD, +0x1AE => 0x288, +0x1AF => 0x1B0, +0x1B1 => 0x28A, +0x1B2 => 0x28B, +0x1B3 => 0x1B4, +0x1B5 => 0x1B6, +0x1B7 => 0x292, +0x1B8 => 0x1B9, +0x1BC => 0x1BD, +0x1C4 => 0x1C6, +0x1C5 => 0x1C6, +0x1C7 => 0x1C9, +0x1C8 => 0x1C9, +0x1CA => 0x1CC, +0x1CB => 0x1CC, +0x1CD => 0x1CE, +0x1CF => 0x1D0, +0x1D1 => 0x1D2, +0x1D3 => 0x1D4, +0x1D5 => 0x1D6, +0x1D7 => 0x1D8, +0x1D9 => 0x1DA, +0x1DB => 0x1DC, +0x1DE => 0x1DF, +0x1E0 => 0x1E1, +0x1E2 => 0x1E3, +0x1E4 => 0x1E5, +0x1E6 => 0x1E7, +0x1E8 => 0x1E9, +0x1EA => 0x1EB, +0x1EC => 0x1ED, +0x1EE => 0x1EF, +0x1F1 => 0x1F3, +0x1F2 => 0x1F3, +0x1F4 => 0x1F5, +0x1F6 => 0x195, +0x1F7 => 0x1BF, +0x1F8 => 0x1F9, +0x1FA => 0x1FB, +0x1FC => 0x1FD, +0x1FE => 0x1FF, +0x200 => 0x201, +0x202 => 0x203, +0x204 => 0x205, +0x206 => 0x207, +0x208 => 0x209, +0x20A => 0x20B, +0x20C => 0x20D, +0x20E => 0x20F, +0x210 => 0x211, +0x212 => 0x213, +0x214 => 0x215, +0x216 => 0x217, +0x218 => 0x219, +0x21A => 0x21B, +0x21C => 0x21D, +0x21E => 0x21F, +0x220 => 0x19E, +0x222 => 0x223, +0x224 => 0x225, +0x226 => 0x227, +0x228 => 0x229, +0x22A => 0x22B, +0x22C => 0x22D, +0x22E => 0x22F, +0x230 => 0x231, +0x232 => 0x233, +0x23A => 0x2C65, +0x23B => 0x23C, +0x23D => 0x19A, +0x23E => 0x2C66, +0x241 => 0x242, +0x243 => 0x180, +0x244 => 0x289, +0x245 => 0x28C, +0x246 => 0x247, +0x248 => 0x249, +0x24A => 0x24B, +0x24C => 0x24D, +0x24E => 0x24F, +0x370 => 0x371, +0x372 => 0x373, +0x376 => 0x377, +0x37F => 0x3F3, +0x386 => 0x3AC, +0x388 => 0x3AD, +0x389 => 0x3AE, +0x38A => 0x3AF, +0x38C => 0x3CC, +0x38E => 0x3CD, +0x38F => 0x3CE, +0x391 => 0x3B1, +0x392 => 0x3B2, +0x393 => 0x3B3, +0x394 => 0x3B4, +0x395 => 0x3B5, +0x396 => 0x3B6, +0x397 => 0x3B7, +0x398 => 0x3B8, +0x399 => 0x3B9, +0x39A => 0x3BA, +0x39B => 0x3BB, +0x39C => 0x3BC, +0x39D => 0x3BD, +0x39E => 0x3BE, +0x39F => 0x3BF, +0x3A0 => 0x3C0, +0x3A1 => 0x3C1, +0x3A3 => 0x3C3, +0x3A4 => 0x3C4, +0x3A5 => 0x3C5, +0x3A6 => 0x3C6, +0x3A7 => 0x3C7, +0x3A8 => 0x3C8, +0x3A9 => 0x3C9, +0x3AA => 0x3CA, +0x3AB => 0x3CB, +0x3CF => 0x3D7, +0x3D8 => 0x3D9, +0x3DA => 0x3DB, +0x3DC => 0x3DD, +0x3DE => 0x3DF, +0x3E0 => 0x3E1, +0x3E2 => 0x3E3, +0x3E4 => 0x3E5, +0x3E6 => 0x3E7, +0x3E8 => 0x3E9, +0x3EA => 0x3EB, +0x3EC => 0x3ED, +0x3EE => 0x3EF, +0x3F4 => 0x3B8, +0x3F7 => 0x3F8, +0x3F9 => 0x3F2, +0x3FA => 0x3FB, +0x3FD => 0x37B, +0x3FE => 0x37C, +0x3FF => 0x37D, +0x400 => 0x450, +0x401 => 0x451, +0x402 => 0x452, +0x403 => 0x453, +0x404 => 0x454, +0x405 => 0x455, +0x406 => 0x456, +0x407 => 0x457, +0x408 => 0x458, +0x409 => 0x459, +0x40A => 0x45A, +0x40B => 0x45B, +0x40C => 0x45C, +0x40D => 0x45D, +0x40E => 0x45E, +0x40F => 0x45F, +0x410 => 0x430, +0x411 => 0x431, +0x412 => 0x432, +0x413 => 0x433, +0x414 => 0x434, +0x415 => 0x435, +0x416 => 0x436, +0x417 => 0x437, +0x418 => 0x438, +0x419 => 0x439, +0x41A => 0x43A, +0x41B => 0x43B, +0x41C => 0x43C, +0x41D => 0x43D, +0x41E => 0x43E, +0x41F => 0x43F, +0x420 => 0x440, +0x421 => 0x441, +0x422 => 0x442, +0x423 => 0x443, +0x424 => 0x444, +0x425 => 0x445, +0x426 => 0x446, +0x427 => 0x447, +0x428 => 0x448, +0x429 => 0x449, +0x42A => 0x44A, +0x42B => 0x44B, +0x42C => 0x44C, +0x42D => 0x44D, +0x42E => 0x44E, +0x42F => 0x44F, +0x460 => 0x461, +0x462 => 0x463, +0x464 => 0x465, +0x466 => 0x467, +0x468 => 0x469, +0x46A => 0x46B, +0x46C => 0x46D, +0x46E => 0x46F, +0x470 => 0x471, +0x472 => 0x473, +0x474 => 0x475, +0x476 => 0x477, +0x478 => 0x479, +0x47A => 0x47B, +0x47C => 0x47D, +0x47E => 0x47F, +0x480 => 0x481, +0x48A => 0x48B, +0x48C => 0x48D, +0x48E => 0x48F, +0x490 => 0x491, +0x492 => 0x493, +0x494 => 0x495, +0x496 => 0x497, +0x498 => 0x499, +0x49A => 0x49B, +0x49C => 0x49D, +0x49E => 0x49F, +0x4A0 => 0x4A1, +0x4A2 => 0x4A3, +0x4A4 => 0x4A5, +0x4A6 => 0x4A7, +0x4A8 => 0x4A9, +0x4AA => 0x4AB, +0x4AC => 0x4AD, +0x4AE => 0x4AF, +0x4B0 => 0x4B1, +0x4B2 => 0x4B3, +0x4B4 => 0x4B5, +0x4B6 => 0x4B7, +0x4B8 => 0x4B9, +0x4BA => 0x4BB, +0x4BC => 0x4BD, +0x4BE => 0x4BF, +0x4C0 => 0x4CF, +0x4C1 => 0x4C2, +0x4C3 => 0x4C4, +0x4C5 => 0x4C6, +0x4C7 => 0x4C8, +0x4C9 => 0x4CA, +0x4CB => 0x4CC, +0x4CD => 0x4CE, +0x4D0 => 0x4D1, +0x4D2 => 0x4D3, +0x4D4 => 0x4D5, +0x4D6 => 0x4D7, +0x4D8 => 0x4D9, +0x4DA => 0x4DB, +0x4DC => 0x4DD, +0x4DE => 0x4DF, +0x4E0 => 0x4E1, +0x4E2 => 0x4E3, +0x4E4 => 0x4E5, +0x4E6 => 0x4E7, +0x4E8 => 0x4E9, +0x4EA => 0x4EB, +0x4EC => 0x4ED, +0x4EE => 0x4EF, +0x4F0 => 0x4F1, +0x4F2 => 0x4F3, +0x4F4 => 0x4F5, +0x4F6 => 0x4F7, +0x4F8 => 0x4F9, +0x4FA => 0x4FB, +0x4FC => 0x4FD, +0x4FE => 0x4FF, +0x500 => 0x501, +0x502 => 0x503, +0x504 => 0x505, +0x506 => 0x507, +0x508 => 0x509, +0x50A => 0x50B, +0x50C => 0x50D, +0x50E => 0x50F, +0x510 => 0x511, +0x512 => 0x513, +0x514 => 0x515, +0x516 => 0x517, +0x518 => 0x519, +0x51A => 0x51B, +0x51C => 0x51D, +0x51E => 0x51F, +0x520 => 0x521, +0x522 => 0x523, +0x524 => 0x525, +0x526 => 0x527, +0x528 => 0x529, +0x52A => 0x52B, +0x52C => 0x52D, +0x52E => 0x52F, +0x531 => 0x561, +0x532 => 0x562, +0x533 => 0x563, +0x534 => 0x564, +0x535 => 0x565, +0x536 => 0x566, +0x537 => 0x567, +0x538 => 0x568, +0x539 => 0x569, +0x53A => 0x56A, +0x53B => 0x56B, +0x53C => 0x56C, +0x53D => 0x56D, +0x53E => 0x56E, +0x53F => 0x56F, +0x540 => 0x570, +0x541 => 0x571, +0x542 => 0x572, +0x543 => 0x573, +0x544 => 0x574, +0x545 => 0x575, +0x546 => 0x576, +0x547 => 0x577, +0x548 => 0x578, +0x549 => 0x579, +0x54A => 0x57A, +0x54B => 0x57B, +0x54C => 0x57C, +0x54D => 0x57D, +0x54E => 0x57E, +0x54F => 0x57F, +0x550 => 0x580, +0x551 => 0x581, +0x552 => 0x582, +0x553 => 0x583, +0x554 => 0x584, +0x555 => 0x585, +0x556 => 0x586, +0x10A0 => 0x2D00, +0x10A1 => 0x2D01, +0x10A2 => 0x2D02, +0x10A3 => 0x2D03, +0x10A4 => 0x2D04, +0x10A5 => 0x2D05, +0x10A6 => 0x2D06, +0x10A7 => 0x2D07, +0x10A8 => 0x2D08, +0x10A9 => 0x2D09, +0x10AA => 0x2D0A, +0x10AB => 0x2D0B, +0x10AC => 0x2D0C, +0x10AD => 0x2D0D, +0x10AE => 0x2D0E, +0x10AF => 0x2D0F, +0x10B0 => 0x2D10, +0x10B1 => 0x2D11, +0x10B2 => 0x2D12, +0x10B3 => 0x2D13, +0x10B4 => 0x2D14, +0x10B5 => 0x2D15, +0x10B6 => 0x2D16, +0x10B7 => 0x2D17, +0x10B8 => 0x2D18, +0x10B9 => 0x2D19, +0x10BA => 0x2D1A, +0x10BB => 0x2D1B, +0x10BC => 0x2D1C, +0x10BD => 0x2D1D, +0x10BE => 0x2D1E, +0x10BF => 0x2D1F, +0x10C0 => 0x2D20, +0x10C1 => 0x2D21, +0x10C2 => 0x2D22, +0x10C3 => 0x2D23, +0x10C4 => 0x2D24, +0x10C5 => 0x2D25, +0x10C7 => 0x2D27, +0x10CD => 0x2D2D, +0x13A0 => 0xAB70, +0x13A1 => 0xAB71, +0x13A2 => 0xAB72, +0x13A3 => 0xAB73, +0x13A4 => 0xAB74, +0x13A5 => 0xAB75, +0x13A6 => 0xAB76, +0x13A7 => 0xAB77, +0x13A8 => 0xAB78, +0x13A9 => 0xAB79, +0x13AA => 0xAB7A, +0x13AB => 0xAB7B, +0x13AC => 0xAB7C, +0x13AD => 0xAB7D, +0x13AE => 0xAB7E, +0x13AF => 0xAB7F, +0x13B0 => 0xAB80, +0x13B1 => 0xAB81, +0x13B2 => 0xAB82, +0x13B3 => 0xAB83, +0x13B4 => 0xAB84, +0x13B5 => 0xAB85, +0x13B6 => 0xAB86, +0x13B7 => 0xAB87, +0x13B8 => 0xAB88, +0x13B9 => 0xAB89, +0x13BA => 0xAB8A, +0x13BB => 0xAB8B, +0x13BC => 0xAB8C, +0x13BD => 0xAB8D, +0x13BE => 0xAB8E, +0x13BF => 0xAB8F, +0x13C0 => 0xAB90, +0x13C1 => 0xAB91, +0x13C2 => 0xAB92, +0x13C3 => 0xAB93, +0x13C4 => 0xAB94, +0x13C5 => 0xAB95, +0x13C6 => 0xAB96, +0x13C7 => 0xAB97, +0x13C8 => 0xAB98, +0x13C9 => 0xAB99, +0x13CA => 0xAB9A, +0x13CB => 0xAB9B, +0x13CC => 0xAB9C, +0x13CD => 0xAB9D, +0x13CE => 0xAB9E, +0x13CF => 0xAB9F, +0x13D0 => 0xABA0, +0x13D1 => 0xABA1, +0x13D2 => 0xABA2, +0x13D3 => 0xABA3, +0x13D4 => 0xABA4, +0x13D5 => 0xABA5, +0x13D6 => 0xABA6, +0x13D7 => 0xABA7, +0x13D8 => 0xABA8, +0x13D9 => 0xABA9, +0x13DA => 0xABAA, +0x13DB => 0xABAB, +0x13DC => 0xABAC, +0x13DD => 0xABAD, +0x13DE => 0xABAE, +0x13DF => 0xABAF, +0x13E0 => 0xABB0, +0x13E1 => 0xABB1, +0x13E2 => 0xABB2, +0x13E3 => 0xABB3, +0x13E4 => 0xABB4, +0x13E5 => 0xABB5, +0x13E6 => 0xABB6, +0x13E7 => 0xABB7, +0x13E8 => 0xABB8, +0x13E9 => 0xABB9, +0x13EA => 0xABBA, +0x13EB => 0xABBB, +0x13EC => 0xABBC, +0x13ED => 0xABBD, +0x13EE => 0xABBE, +0x13EF => 0xABBF, +0x13F0 => 0x13F8, +0x13F1 => 0x13F9, +0x13F2 => 0x13FA, +0x13F3 => 0x13FB, +0x13F4 => 0x13FC, +0x13F5 => 0x13FD, +0x1C90 => 0x10D0, +0x1C91 => 0x10D1, +0x1C92 => 0x10D2, +0x1C93 => 0x10D3, +0x1C94 => 0x10D4, +0x1C95 => 0x10D5, +0x1C96 => 0x10D6, +0x1C97 => 0x10D7, +0x1C98 => 0x10D8, +0x1C99 => 0x10D9, +0x1C9A => 0x10DA, +0x1C9B => 0x10DB, +0x1C9C => 0x10DC, +0x1C9D => 0x10DD, +0x1C9E => 0x10DE, +0x1C9F => 0x10DF, +0x1CA0 => 0x10E0, +0x1CA1 => 0x10E1, +0x1CA2 => 0x10E2, +0x1CA3 => 0x10E3, +0x1CA4 => 0x10E4, +0x1CA5 => 0x10E5, +0x1CA6 => 0x10E6, +0x1CA7 => 0x10E7, +0x1CA8 => 0x10E8, +0x1CA9 => 0x10E9, +0x1CAA => 0x10EA, +0x1CAB => 0x10EB, +0x1CAC => 0x10EC, +0x1CAD => 0x10ED, +0x1CAE => 0x10EE, +0x1CAF => 0x10EF, +0x1CB0 => 0x10F0, +0x1CB1 => 0x10F1, +0x1CB2 => 0x10F2, +0x1CB3 => 0x10F3, +0x1CB4 => 0x10F4, +0x1CB5 => 0x10F5, +0x1CB6 => 0x10F6, +0x1CB7 => 0x10F7, +0x1CB8 => 0x10F8, +0x1CB9 => 0x10F9, +0x1CBA => 0x10FA, +0x1CBD => 0x10FD, +0x1CBE => 0x10FE, +0x1CBF => 0x10FF, +0x1E00 => 0x1E01, +0x1E02 => 0x1E03, +0x1E04 => 0x1E05, +0x1E06 => 0x1E07, +0x1E08 => 0x1E09, +0x1E0A => 0x1E0B, +0x1E0C => 0x1E0D, +0x1E0E => 0x1E0F, +0x1E10 => 0x1E11, +0x1E12 => 0x1E13, +0x1E14 => 0x1E15, +0x1E16 => 0x1E17, +0x1E18 => 0x1E19, +0x1E1A => 0x1E1B, +0x1E1C => 0x1E1D, +0x1E1E => 0x1E1F, +0x1E20 => 0x1E21, +0x1E22 => 0x1E23, +0x1E24 => 0x1E25, +0x1E26 => 0x1E27, +0x1E28 => 0x1E29, +0x1E2A => 0x1E2B, +0x1E2C => 0x1E2D, +0x1E2E => 0x1E2F, +0x1E30 => 0x1E31, +0x1E32 => 0x1E33, +0x1E34 => 0x1E35, +0x1E36 => 0x1E37, +0x1E38 => 0x1E39, +0x1E3A => 0x1E3B, +0x1E3C => 0x1E3D, +0x1E3E => 0x1E3F, +0x1E40 => 0x1E41, +0x1E42 => 0x1E43, +0x1E44 => 0x1E45, +0x1E46 => 0x1E47, +0x1E48 => 0x1E49, +0x1E4A => 0x1E4B, +0x1E4C => 0x1E4D, +0x1E4E => 0x1E4F, +0x1E50 => 0x1E51, +0x1E52 => 0x1E53, +0x1E54 => 0x1E55, +0x1E56 => 0x1E57, +0x1E58 => 0x1E59, +0x1E5A => 0x1E5B, +0x1E5C => 0x1E5D, +0x1E5E => 0x1E5F, +0x1E60 => 0x1E61, +0x1E62 => 0x1E63, +0x1E64 => 0x1E65, +0x1E66 => 0x1E67, +0x1E68 => 0x1E69, +0x1E6A => 0x1E6B, +0x1E6C => 0x1E6D, +0x1E6E => 0x1E6F, +0x1E70 => 0x1E71, +0x1E72 => 0x1E73, +0x1E74 => 0x1E75, +0x1E76 => 0x1E77, +0x1E78 => 0x1E79, +0x1E7A => 0x1E7B, +0x1E7C => 0x1E7D, +0x1E7E => 0x1E7F, +0x1E80 => 0x1E81, +0x1E82 => 0x1E83, +0x1E84 => 0x1E85, +0x1E86 => 0x1E87, +0x1E88 => 0x1E89, +0x1E8A => 0x1E8B, +0x1E8C => 0x1E8D, +0x1E8E => 0x1E8F, +0x1E90 => 0x1E91, +0x1E92 => 0x1E93, +0x1E94 => 0x1E95, +0x1E9E => 0xDF, +0x1EA0 => 0x1EA1, +0x1EA2 => 0x1EA3, +0x1EA4 => 0x1EA5, +0x1EA6 => 0x1EA7, +0x1EA8 => 0x1EA9, +0x1EAA => 0x1EAB, +0x1EAC => 0x1EAD, +0x1EAE => 0x1EAF, +0x1EB0 => 0x1EB1, +0x1EB2 => 0x1EB3, +0x1EB4 => 0x1EB5, +0x1EB6 => 0x1EB7, +0x1EB8 => 0x1EB9, +0x1EBA => 0x1EBB, +0x1EBC => 0x1EBD, +0x1EBE => 0x1EBF, +0x1EC0 => 0x1EC1, +0x1EC2 => 0x1EC3, +0x1EC4 => 0x1EC5, +0x1EC6 => 0x1EC7, +0x1EC8 => 0x1EC9, +0x1ECA => 0x1ECB, +0x1ECC => 0x1ECD, +0x1ECE => 0x1ECF, +0x1ED0 => 0x1ED1, +0x1ED2 => 0x1ED3, +0x1ED4 => 0x1ED5, +0x1ED6 => 0x1ED7, +0x1ED8 => 0x1ED9, +0x1EDA => 0x1EDB, +0x1EDC => 0x1EDD, +0x1EDE => 0x1EDF, +0x1EE0 => 0x1EE1, +0x1EE2 => 0x1EE3, +0x1EE4 => 0x1EE5, +0x1EE6 => 0x1EE7, +0x1EE8 => 0x1EE9, +0x1EEA => 0x1EEB, +0x1EEC => 0x1EED, +0x1EEE => 0x1EEF, +0x1EF0 => 0x1EF1, +0x1EF2 => 0x1EF3, +0x1EF4 => 0x1EF5, +0x1EF6 => 0x1EF7, +0x1EF8 => 0x1EF9, +0x1EFA => 0x1EFB, +0x1EFC => 0x1EFD, +0x1EFE => 0x1EFF, +0x1F08 => 0x1F00, +0x1F09 => 0x1F01, +0x1F0A => 0x1F02, +0x1F0B => 0x1F03, +0x1F0C => 0x1F04, +0x1F0D => 0x1F05, +0x1F0E => 0x1F06, +0x1F0F => 0x1F07, +0x1F18 => 0x1F10, +0x1F19 => 0x1F11, +0x1F1A => 0x1F12, +0x1F1B => 0x1F13, +0x1F1C => 0x1F14, +0x1F1D => 0x1F15, +0x1F28 => 0x1F20, +0x1F29 => 0x1F21, +0x1F2A => 0x1F22, +0x1F2B => 0x1F23, +0x1F2C => 0x1F24, +0x1F2D => 0x1F25, +0x1F2E => 0x1F26, +0x1F2F => 0x1F27, +0x1F38 => 0x1F30, +0x1F39 => 0x1F31, +0x1F3A => 0x1F32, +0x1F3B => 0x1F33, +0x1F3C => 0x1F34, +0x1F3D => 0x1F35, +0x1F3E => 0x1F36, +0x1F3F => 0x1F37, +0x1F48 => 0x1F40, +0x1F49 => 0x1F41, +0x1F4A => 0x1F42, +0x1F4B => 0x1F43, +0x1F4C => 0x1F44, +0x1F4D => 0x1F45, +0x1F59 => 0x1F51, +0x1F5B => 0x1F53, +0x1F5D => 0x1F55, +0x1F5F => 0x1F57, +0x1F68 => 0x1F60, +0x1F69 => 0x1F61, +0x1F6A => 0x1F62, +0x1F6B => 0x1F63, +0x1F6C => 0x1F64, +0x1F6D => 0x1F65, +0x1F6E => 0x1F66, +0x1F6F => 0x1F67, +0x1F88 => 0x1F80, +0x1F89 => 0x1F81, +0x1F8A => 0x1F82, +0x1F8B => 0x1F83, +0x1F8C => 0x1F84, +0x1F8D => 0x1F85, +0x1F8E => 0x1F86, +0x1F8F => 0x1F87, +0x1F98 => 0x1F90, +0x1F99 => 0x1F91, +0x1F9A => 0x1F92, +0x1F9B => 0x1F93, +0x1F9C => 0x1F94, +0x1F9D => 0x1F95, +0x1F9E => 0x1F96, +0x1F9F => 0x1F97, +0x1FA8 => 0x1FA0, +0x1FA9 => 0x1FA1, +0x1FAA => 0x1FA2, +0x1FAB => 0x1FA3, +0x1FAC => 0x1FA4, +0x1FAD => 0x1FA5, +0x1FAE => 0x1FA6, +0x1FAF => 0x1FA7, +0x1FB8 => 0x1FB0, +0x1FB9 => 0x1FB1, +0x1FBA => 0x1F70, +0x1FBB => 0x1F71, +0x1FBC => 0x1FB3, +0x1FC8 => 0x1F72, +0x1FC9 => 0x1F73, +0x1FCA => 0x1F74, +0x1FCB => 0x1F75, +0x1FCC => 0x1FC3, +0x1FD8 => 0x1FD0, +0x1FD9 => 0x1FD1, +0x1FDA => 0x1F76, +0x1FDB => 0x1F77, +0x1FE8 => 0x1FE0, +0x1FE9 => 0x1FE1, +0x1FEA => 0x1F7A, +0x1FEB => 0x1F7B, +0x1FEC => 0x1FE5, +0x1FF8 => 0x1F78, +0x1FF9 => 0x1F79, +0x1FFA => 0x1F7C, +0x1FFB => 0x1F7D, +0x1FFC => 0x1FF3, +0x2126 => 0x3C9, +0x212A => 0x6B, +0x212B => 0xE5, +0x2132 => 0x214E, +0x2160 => 0x2170, +0x2161 => 0x2171, +0x2162 => 0x2172, +0x2163 => 0x2173, +0x2164 => 0x2174, +0x2165 => 0x2175, +0x2166 => 0x2176, +0x2167 => 0x2177, +0x2168 => 0x2178, +0x2169 => 0x2179, +0x216A => 0x217A, +0x216B => 0x217B, +0x216C => 0x217C, +0x216D => 0x217D, +0x216E => 0x217E, +0x216F => 0x217F, +0x2183 => 0x2184, +0x24B6 => 0x24D0, +0x24B7 => 0x24D1, +0x24B8 => 0x24D2, +0x24B9 => 0x24D3, +0x24BA => 0x24D4, +0x24BB => 0x24D5, +0x24BC => 0x24D6, +0x24BD => 0x24D7, +0x24BE => 0x24D8, +0x24BF => 0x24D9, +0x24C0 => 0x24DA, +0x24C1 => 0x24DB, +0x24C2 => 0x24DC, +0x24C3 => 0x24DD, +0x24C4 => 0x24DE, +0x24C5 => 0x24DF, +0x24C6 => 0x24E0, +0x24C7 => 0x24E1, +0x24C8 => 0x24E2, +0x24C9 => 0x24E3, +0x24CA => 0x24E4, +0x24CB => 0x24E5, +0x24CC => 0x24E6, +0x24CD => 0x24E7, +0x24CE => 0x24E8, +0x24CF => 0x24E9, +0x2C00 => 0x2C30, +0x2C01 => 0x2C31, +0x2C02 => 0x2C32, +0x2C03 => 0x2C33, +0x2C04 => 0x2C34, +0x2C05 => 0x2C35, +0x2C06 => 0x2C36, +0x2C07 => 0x2C37, +0x2C08 => 0x2C38, +0x2C09 => 0x2C39, +0x2C0A => 0x2C3A, +0x2C0B => 0x2C3B, +0x2C0C => 0x2C3C, +0x2C0D => 0x2C3D, +0x2C0E => 0x2C3E, +0x2C0F => 0x2C3F, +0x2C10 => 0x2C40, +0x2C11 => 0x2C41, +0x2C12 => 0x2C42, +0x2C13 => 0x2C43, +0x2C14 => 0x2C44, +0x2C15 => 0x2C45, +0x2C16 => 0x2C46, +0x2C17 => 0x2C47, +0x2C18 => 0x2C48, +0x2C19 => 0x2C49, +0x2C1A => 0x2C4A, +0x2C1B => 0x2C4B, +0x2C1C => 0x2C4C, +0x2C1D => 0x2C4D, +0x2C1E => 0x2C4E, +0x2C1F => 0x2C4F, +0x2C20 => 0x2C50, +0x2C21 => 0x2C51, +0x2C22 => 0x2C52, +0x2C23 => 0x2C53, +0x2C24 => 0x2C54, +0x2C25 => 0x2C55, +0x2C26 => 0x2C56, +0x2C27 => 0x2C57, +0x2C28 => 0x2C58, +0x2C29 => 0x2C59, +0x2C2A => 0x2C5A, +0x2C2B => 0x2C5B, +0x2C2C => 0x2C5C, +0x2C2D => 0x2C5D, +0x2C2E => 0x2C5E, +0x2C60 => 0x2C61, +0x2C62 => 0x26B, +0x2C63 => 0x1D7D, +0x2C64 => 0x27D, +0x2C67 => 0x2C68, +0x2C69 => 0x2C6A, +0x2C6B => 0x2C6C, +0x2C6D => 0x251, +0x2C6E => 0x271, +0x2C6F => 0x250, +0x2C70 => 0x252, +0x2C72 => 0x2C73, +0x2C75 => 0x2C76, +0x2C7E => 0x23F, +0x2C7F => 0x240, +0x2C80 => 0x2C81, +0x2C82 => 0x2C83, +0x2C84 => 0x2C85, +0x2C86 => 0x2C87, +0x2C88 => 0x2C89, +0x2C8A => 0x2C8B, +0x2C8C => 0x2C8D, +0x2C8E => 0x2C8F, +0x2C90 => 0x2C91, +0x2C92 => 0x2C93, +0x2C94 => 0x2C95, +0x2C96 => 0x2C97, +0x2C98 => 0x2C99, +0x2C9A => 0x2C9B, +0x2C9C => 0x2C9D, +0x2C9E => 0x2C9F, +0x2CA0 => 0x2CA1, +0x2CA2 => 0x2CA3, +0x2CA4 => 0x2CA5, +0x2CA6 => 0x2CA7, +0x2CA8 => 0x2CA9, +0x2CAA => 0x2CAB, +0x2CAC => 0x2CAD, +0x2CAE => 0x2CAF, +0x2CB0 => 0x2CB1, +0x2CB2 => 0x2CB3, +0x2CB4 => 0x2CB5, +0x2CB6 => 0x2CB7, +0x2CB8 => 0x2CB9, +0x2CBA => 0x2CBB, +0x2CBC => 0x2CBD, +0x2CBE => 0x2CBF, +0x2CC0 => 0x2CC1, +0x2CC2 => 0x2CC3, +0x2CC4 => 0x2CC5, +0x2CC6 => 0x2CC7, +0x2CC8 => 0x2CC9, +0x2CCA => 0x2CCB, +0x2CCC => 0x2CCD, +0x2CCE => 0x2CCF, +0x2CD0 => 0x2CD1, +0x2CD2 => 0x2CD3, +0x2CD4 => 0x2CD5, +0x2CD6 => 0x2CD7, +0x2CD8 => 0x2CD9, +0x2CDA => 0x2CDB, +0x2CDC => 0x2CDD, +0x2CDE => 0x2CDF, +0x2CE0 => 0x2CE1, +0x2CE2 => 0x2CE3, +0x2CEB => 0x2CEC, +0x2CED => 0x2CEE, +0x2CF2 => 0x2CF3, +0xA640 => 0xA641, +0xA642 => 0xA643, +0xA644 => 0xA645, +0xA646 => 0xA647, +0xA648 => 0xA649, +0xA64A => 0xA64B, +0xA64C => 0xA64D, +0xA64E => 0xA64F, +0xA650 => 0xA651, +0xA652 => 0xA653, +0xA654 => 0xA655, +0xA656 => 0xA657, +0xA658 => 0xA659, +0xA65A => 0xA65B, +0xA65C => 0xA65D, +0xA65E => 0xA65F, +0xA660 => 0xA661, +0xA662 => 0xA663, +0xA664 => 0xA665, +0xA666 => 0xA667, +0xA668 => 0xA669, +0xA66A => 0xA66B, +0xA66C => 0xA66D, +0xA680 => 0xA681, +0xA682 => 0xA683, +0xA684 => 0xA685, +0xA686 => 0xA687, +0xA688 => 0xA689, +0xA68A => 0xA68B, +0xA68C => 0xA68D, +0xA68E => 0xA68F, +0xA690 => 0xA691, +0xA692 => 0xA693, +0xA694 => 0xA695, +0xA696 => 0xA697, +0xA698 => 0xA699, +0xA69A => 0xA69B, +0xA722 => 0xA723, +0xA724 => 0xA725, +0xA726 => 0xA727, +0xA728 => 0xA729, +0xA72A => 0xA72B, +0xA72C => 0xA72D, +0xA72E => 0xA72F, +0xA732 => 0xA733, +0xA734 => 0xA735, +0xA736 => 0xA737, +0xA738 => 0xA739, +0xA73A => 0xA73B, +0xA73C => 0xA73D, +0xA73E => 0xA73F, +0xA740 => 0xA741, +0xA742 => 0xA743, +0xA744 => 0xA745, +0xA746 => 0xA747, +0xA748 => 0xA749, +0xA74A => 0xA74B, +0xA74C => 0xA74D, +0xA74E => 0xA74F, +0xA750 => 0xA751, +0xA752 => 0xA753, +0xA754 => 0xA755, +0xA756 => 0xA757, +0xA758 => 0xA759, +0xA75A => 0xA75B, +0xA75C => 0xA75D, +0xA75E => 0xA75F, +0xA760 => 0xA761, +0xA762 => 0xA763, +0xA764 => 0xA765, +0xA766 => 0xA767, +0xA768 => 0xA769, +0xA76A => 0xA76B, +0xA76C => 0xA76D, +0xA76E => 0xA76F, +0xA779 => 0xA77A, +0xA77B => 0xA77C, +0xA77D => 0x1D79, +0xA77E => 0xA77F, +0xA780 => 0xA781, +0xA782 => 0xA783, +0xA784 => 0xA785, +0xA786 => 0xA787, +0xA78B => 0xA78C, +0xA78D => 0x265, +0xA790 => 0xA791, +0xA792 => 0xA793, +0xA796 => 0xA797, +0xA798 => 0xA799, +0xA79A => 0xA79B, +0xA79C => 0xA79D, +0xA79E => 0xA79F, +0xA7A0 => 0xA7A1, +0xA7A2 => 0xA7A3, +0xA7A4 => 0xA7A5, +0xA7A6 => 0xA7A7, +0xA7A8 => 0xA7A9, +0xA7AA => 0x266, +0xA7AB => 0x25C, +0xA7AC => 0x261, +0xA7AD => 0x26C, +0xA7AE => 0x26A, +0xA7B0 => 0x29E, +0xA7B1 => 0x287, +0xA7B2 => 0x29D, +0xA7B3 => 0xAB53, +0xA7B4 => 0xA7B5, +0xA7B6 => 0xA7B7, +0xA7B8 => 0xA7B9, +0xA7BA => 0xA7BB, +0xA7BC => 0xA7BD, +0xA7BE => 0xA7BF, +0xA7C2 => 0xA7C3, +0xA7C4 => 0xA794, +0xA7C5 => 0x282, +0xA7C6 => 0x1D8E, +0xA7C7 => 0xA7C8, +0xA7C9 => 0xA7CA, +0xA7F5 => 0xA7F6, +0xFF21 => 0xFF41, +0xFF22 => 0xFF42, +0xFF23 => 0xFF43, +0xFF24 => 0xFF44, +0xFF25 => 0xFF45, +0xFF26 => 0xFF46, +0xFF27 => 0xFF47, +0xFF28 => 0xFF48, +0xFF29 => 0xFF49, +0xFF2A => 0xFF4A, +0xFF2B => 0xFF4B, +0xFF2C => 0xFF4C, +0xFF2D => 0xFF4D, +0xFF2E => 0xFF4E, +0xFF2F => 0xFF4F, +0xFF30 => 0xFF50, +0xFF31 => 0xFF51, +0xFF32 => 0xFF52, +0xFF33 => 0xFF53, +0xFF34 => 0xFF54, +0xFF35 => 0xFF55, +0xFF36 => 0xFF56, +0xFF37 => 0xFF57, +0xFF38 => 0xFF58, +0xFF39 => 0xFF59, +0xFF3A => 0xFF5A, +0x10400 => 0x10428, +0x10401 => 0x10429, +0x10402 => 0x1042A, +0x10403 => 0x1042B, +0x10404 => 0x1042C, +0x10405 => 0x1042D, +0x10406 => 0x1042E, +0x10407 => 0x1042F, +0x10408 => 0x10430, +0x10409 => 0x10431, +0x1040A => 0x10432, +0x1040B => 0x10433, +0x1040C => 0x10434, +0x1040D => 0x10435, +0x1040E => 0x10436, +0x1040F => 0x10437, +0x10410 => 0x10438, +0x10411 => 0x10439, +0x10412 => 0x1043A, +0x10413 => 0x1043B, +0x10414 => 0x1043C, +0x10415 => 0x1043D, +0x10416 => 0x1043E, +0x10417 => 0x1043F, +0x10418 => 0x10440, +0x10419 => 0x10441, +0x1041A => 0x10442, +0x1041B => 0x10443, +0x1041C => 0x10444, +0x1041D => 0x10445, +0x1041E => 0x10446, +0x1041F => 0x10447, +0x10420 => 0x10448, +0x10421 => 0x10449, +0x10422 => 0x1044A, +0x10423 => 0x1044B, +0x10424 => 0x1044C, +0x10425 => 0x1044D, +0x10426 => 0x1044E, +0x10427 => 0x1044F, +0x104B0 => 0x104D8, +0x104B1 => 0x104D9, +0x104B2 => 0x104DA, +0x104B3 => 0x104DB, +0x104B4 => 0x104DC, +0x104B5 => 0x104DD, +0x104B6 => 0x104DE, +0x104B7 => 0x104DF, +0x104B8 => 0x104E0, +0x104B9 => 0x104E1, +0x104BA => 0x104E2, +0x104BB => 0x104E3, +0x104BC => 0x104E4, +0x104BD => 0x104E5, +0x104BE => 0x104E6, +0x104BF => 0x104E7, +0x104C0 => 0x104E8, +0x104C1 => 0x104E9, +0x104C2 => 0x104EA, +0x104C3 => 0x104EB, +0x104C4 => 0x104EC, +0x104C5 => 0x104ED, +0x104C6 => 0x104EE, +0x104C7 => 0x104EF, +0x104C8 => 0x104F0, +0x104C9 => 0x104F1, +0x104CA => 0x104F2, +0x104CB => 0x104F3, +0x104CC => 0x104F4, +0x104CD => 0x104F5, +0x104CE => 0x104F6, +0x104CF => 0x104F7, +0x104D0 => 0x104F8, +0x104D1 => 0x104F9, +0x104D2 => 0x104FA, +0x104D3 => 0x104FB, +0x10C80 => 0x10CC0, +0x10C81 => 0x10CC1, +0x10C82 => 0x10CC2, +0x10C83 => 0x10CC3, +0x10C84 => 0x10CC4, +0x10C85 => 0x10CC5, +0x10C86 => 0x10CC6, +0x10C87 => 0x10CC7, +0x10C88 => 0x10CC8, +0x10C89 => 0x10CC9, +0x10C8A => 0x10CCA, +0x10C8B => 0x10CCB, +0x10C8C => 0x10CCC, +0x10C8D => 0x10CCD, +0x10C8E => 0x10CCE, +0x10C8F => 0x10CCF, +0x10C90 => 0x10CD0, +0x10C91 => 0x10CD1, +0x10C92 => 0x10CD2, +0x10C93 => 0x10CD3, +0x10C94 => 0x10CD4, +0x10C95 => 0x10CD5, +0x10C96 => 0x10CD6, +0x10C97 => 0x10CD7, +0x10C98 => 0x10CD8, +0x10C99 => 0x10CD9, +0x10C9A => 0x10CDA, +0x10C9B => 0x10CDB, +0x10C9C => 0x10CDC, +0x10C9D => 0x10CDD, +0x10C9E => 0x10CDE, +0x10C9F => 0x10CDF, +0x10CA0 => 0x10CE0, +0x10CA1 => 0x10CE1, +0x10CA2 => 0x10CE2, +0x10CA3 => 0x10CE3, +0x10CA4 => 0x10CE4, +0x10CA5 => 0x10CE5, +0x10CA6 => 0x10CE6, +0x10CA7 => 0x10CE7, +0x10CA8 => 0x10CE8, +0x10CA9 => 0x10CE9, +0x10CAA => 0x10CEA, +0x10CAB => 0x10CEB, +0x10CAC => 0x10CEC, +0x10CAD => 0x10CED, +0x10CAE => 0x10CEE, +0x10CAF => 0x10CEF, +0x10CB0 => 0x10CF0, +0x10CB1 => 0x10CF1, +0x10CB2 => 0x10CF2, +0x118A0 => 0x118C0, +0x118A1 => 0x118C1, +0x118A2 => 0x118C2, +0x118A3 => 0x118C3, +0x118A4 => 0x118C4, +0x118A5 => 0x118C5, +0x118A6 => 0x118C6, +0x118A7 => 0x118C7, +0x118A8 => 0x118C8, +0x118A9 => 0x118C9, +0x118AA => 0x118CA, +0x118AB => 0x118CB, +0x118AC => 0x118CC, +0x118AD => 0x118CD, +0x118AE => 0x118CE, +0x118AF => 0x118CF, +0x118B0 => 0x118D0, +0x118B1 => 0x118D1, +0x118B2 => 0x118D2, +0x118B3 => 0x118D3, +0x118B4 => 0x118D4, +0x118B5 => 0x118D5, +0x118B6 => 0x118D6, +0x118B7 => 0x118D7, +0x118B8 => 0x118D8, +0x118B9 => 0x118D9, +0x118BA => 0x118DA, +0x118BB => 0x118DB, +0x118BC => 0x118DC, +0x118BD => 0x118DD, +0x118BE => 0x118DE, +0x118BF => 0x118DF, +0x16E40 => 0x16E60, +0x16E41 => 0x16E61, +0x16E42 => 0x16E62, +0x16E43 => 0x16E63, +0x16E44 => 0x16E64, +0x16E45 => 0x16E65, +0x16E46 => 0x16E66, +0x16E47 => 0x16E67, +0x16E48 => 0x16E68, +0x16E49 => 0x16E69, +0x16E4A => 0x16E6A, +0x16E4B => 0x16E6B, +0x16E4C => 0x16E6C, +0x16E4D => 0x16E6D, +0x16E4E => 0x16E6E, +0x16E4F => 0x16E6F, +0x16E50 => 0x16E70, +0x16E51 => 0x16E71, +0x16E52 => 0x16E72, +0x16E53 => 0x16E73, +0x16E54 => 0x16E74, +0x16E55 => 0x16E75, +0x16E56 => 0x16E76, +0x16E57 => 0x16E77, +0x16E58 => 0x16E78, +0x16E59 => 0x16E79, +0x16E5A => 0x16E7A, +0x16E5B => 0x16E7B, +0x16E5C => 0x16E7C, +0x16E5D => 0x16E7D, +0x16E5E => 0x16E7E, +0x16E5F => 0x16E7F, +0x1E900 => 0x1E922, +0x1E901 => 0x1E923, +0x1E902 => 0x1E924, +0x1E903 => 0x1E925, +0x1E904 => 0x1E926, +0x1E905 => 0x1E927, +0x1E906 => 0x1E928, +0x1E907 => 0x1E929, +0x1E908 => 0x1E92A, +0x1E909 => 0x1E92B, +0x1E90A => 0x1E92C, +0x1E90B => 0x1E92D, +0x1E90C => 0x1E92E, +0x1E90D => 0x1E92F, +0x1E90E => 0x1E930, +0x1E90F => 0x1E931, +0x1E910 => 0x1E932, +0x1E911 => 0x1E933, +0x1E912 => 0x1E934, +0x1E913 => 0x1E935, +0x1E914 => 0x1E936, +0x1E915 => 0x1E937, +0x1E916 => 0x1E938, +0x1E917 => 0x1E939, +0x1E918 => 0x1E93A, +0x1E919 => 0x1E93B, +0x1E91A => 0x1E93C, +0x1E91B => 0x1E93D, +0x1E91C => 0x1E93E, +0x1E91D => 0x1E93F, +0x1E91E => 0x1E940, +0x1E91F => 0x1E941, +0x1E920 => 0x1E942, +0x1E921 => 0x1E943, +]; diff --git a/vendor/opis/string/res/upper.php b/vendor/opis/string/res/upper.php new file mode 100644 index 000000000..376ce6e6b --- /dev/null +++ b/vendor/opis/string/res/upper.php @@ -0,0 +1,1413 @@ + 0x41, +0x62 => 0x42, +0x63 => 0x43, +0x64 => 0x44, +0x65 => 0x45, +0x66 => 0x46, +0x67 => 0x47, +0x68 => 0x48, +0x69 => 0x49, +0x6A => 0x4A, +0x6B => 0x4B, +0x6C => 0x4C, +0x6D => 0x4D, +0x6E => 0x4E, +0x6F => 0x4F, +0x70 => 0x50, +0x71 => 0x51, +0x72 => 0x52, +0x73 => 0x53, +0x74 => 0x54, +0x75 => 0x55, +0x76 => 0x56, +0x77 => 0x57, +0x78 => 0x58, +0x79 => 0x59, +0x7A => 0x5A, +0xB5 => 0x39C, +0xE0 => 0xC0, +0xE1 => 0xC1, +0xE2 => 0xC2, +0xE3 => 0xC3, +0xE4 => 0xC4, +0xE5 => 0xC5, +0xE6 => 0xC6, +0xE7 => 0xC7, +0xE8 => 0xC8, +0xE9 => 0xC9, +0xEA => 0xCA, +0xEB => 0xCB, +0xEC => 0xCC, +0xED => 0xCD, +0xEE => 0xCE, +0xEF => 0xCF, +0xF0 => 0xD0, +0xF1 => 0xD1, +0xF2 => 0xD2, +0xF3 => 0xD3, +0xF4 => 0xD4, +0xF5 => 0xD5, +0xF6 => 0xD6, +0xF8 => 0xD8, +0xF9 => 0xD9, +0xFA => 0xDA, +0xFB => 0xDB, +0xFC => 0xDC, +0xFD => 0xDD, +0xFE => 0xDE, +0xFF => 0x178, +0x101 => 0x100, +0x103 => 0x102, +0x105 => 0x104, +0x107 => 0x106, +0x109 => 0x108, +0x10B => 0x10A, +0x10D => 0x10C, +0x10F => 0x10E, +0x111 => 0x110, +0x113 => 0x112, +0x115 => 0x114, +0x117 => 0x116, +0x119 => 0x118, +0x11B => 0x11A, +0x11D => 0x11C, +0x11F => 0x11E, +0x121 => 0x120, +0x123 => 0x122, +0x125 => 0x124, +0x127 => 0x126, +0x129 => 0x128, +0x12B => 0x12A, +0x12D => 0x12C, +0x12F => 0x12E, +0x131 => 0x49, +0x133 => 0x132, +0x135 => 0x134, +0x137 => 0x136, +0x13A => 0x139, +0x13C => 0x13B, +0x13E => 0x13D, +0x140 => 0x13F, +0x142 => 0x141, +0x144 => 0x143, +0x146 => 0x145, +0x148 => 0x147, +0x14B => 0x14A, +0x14D => 0x14C, +0x14F => 0x14E, +0x151 => 0x150, +0x153 => 0x152, +0x155 => 0x154, +0x157 => 0x156, +0x159 => 0x158, +0x15B => 0x15A, +0x15D => 0x15C, +0x15F => 0x15E, +0x161 => 0x160, +0x163 => 0x162, +0x165 => 0x164, +0x167 => 0x166, +0x169 => 0x168, +0x16B => 0x16A, +0x16D => 0x16C, +0x16F => 0x16E, +0x171 => 0x170, +0x173 => 0x172, +0x175 => 0x174, +0x177 => 0x176, +0x17A => 0x179, +0x17C => 0x17B, +0x17E => 0x17D, +0x17F => 0x53, +0x180 => 0x243, +0x183 => 0x182, +0x185 => 0x184, +0x188 => 0x187, +0x18C => 0x18B, +0x192 => 0x191, +0x195 => 0x1F6, +0x199 => 0x198, +0x19A => 0x23D, +0x19E => 0x220, +0x1A1 => 0x1A0, +0x1A3 => 0x1A2, +0x1A5 => 0x1A4, +0x1A8 => 0x1A7, +0x1AD => 0x1AC, +0x1B0 => 0x1AF, +0x1B4 => 0x1B3, +0x1B6 => 0x1B5, +0x1B9 => 0x1B8, +0x1BD => 0x1BC, +0x1BF => 0x1F7, +0x1C5 => 0x1C4, +0x1C6 => 0x1C4, +0x1C8 => 0x1C7, +0x1C9 => 0x1C7, +0x1CB => 0x1CA, +0x1CC => 0x1CA, +0x1CE => 0x1CD, +0x1D0 => 0x1CF, +0x1D2 => 0x1D1, +0x1D4 => 0x1D3, +0x1D6 => 0x1D5, +0x1D8 => 0x1D7, +0x1DA => 0x1D9, +0x1DC => 0x1DB, +0x1DD => 0x18E, +0x1DF => 0x1DE, +0x1E1 => 0x1E0, +0x1E3 => 0x1E2, +0x1E5 => 0x1E4, +0x1E7 => 0x1E6, +0x1E9 => 0x1E8, +0x1EB => 0x1EA, +0x1ED => 0x1EC, +0x1EF => 0x1EE, +0x1F2 => 0x1F1, +0x1F3 => 0x1F1, +0x1F5 => 0x1F4, +0x1F9 => 0x1F8, +0x1FB => 0x1FA, +0x1FD => 0x1FC, +0x1FF => 0x1FE, +0x201 => 0x200, +0x203 => 0x202, +0x205 => 0x204, +0x207 => 0x206, +0x209 => 0x208, +0x20B => 0x20A, +0x20D => 0x20C, +0x20F => 0x20E, +0x211 => 0x210, +0x213 => 0x212, +0x215 => 0x214, +0x217 => 0x216, +0x219 => 0x218, +0x21B => 0x21A, +0x21D => 0x21C, +0x21F => 0x21E, +0x223 => 0x222, +0x225 => 0x224, +0x227 => 0x226, +0x229 => 0x228, +0x22B => 0x22A, +0x22D => 0x22C, +0x22F => 0x22E, +0x231 => 0x230, +0x233 => 0x232, +0x23C => 0x23B, +0x23F => 0x2C7E, +0x240 => 0x2C7F, +0x242 => 0x241, +0x247 => 0x246, +0x249 => 0x248, +0x24B => 0x24A, +0x24D => 0x24C, +0x24F => 0x24E, +0x250 => 0x2C6F, +0x251 => 0x2C6D, +0x252 => 0x2C70, +0x253 => 0x181, +0x254 => 0x186, +0x256 => 0x189, +0x257 => 0x18A, +0x259 => 0x18F, +0x25B => 0x190, +0x25C => 0xA7AB, +0x260 => 0x193, +0x261 => 0xA7AC, +0x263 => 0x194, +0x265 => 0xA78D, +0x266 => 0xA7AA, +0x268 => 0x197, +0x269 => 0x196, +0x26A => 0xA7AE, +0x26B => 0x2C62, +0x26C => 0xA7AD, +0x26F => 0x19C, +0x271 => 0x2C6E, +0x272 => 0x19D, +0x275 => 0x19F, +0x27D => 0x2C64, +0x280 => 0x1A6, +0x282 => 0xA7C5, +0x283 => 0x1A9, +0x287 => 0xA7B1, +0x288 => 0x1AE, +0x289 => 0x244, +0x28A => 0x1B1, +0x28B => 0x1B2, +0x28C => 0x245, +0x292 => 0x1B7, +0x29D => 0xA7B2, +0x29E => 0xA7B0, +0x345 => 0x399, +0x371 => 0x370, +0x373 => 0x372, +0x377 => 0x376, +0x37B => 0x3FD, +0x37C => 0x3FE, +0x37D => 0x3FF, +0x3AC => 0x386, +0x3AD => 0x388, +0x3AE => 0x389, +0x3AF => 0x38A, +0x3B1 => 0x391, +0x3B2 => 0x392, +0x3B3 => 0x393, +0x3B4 => 0x394, +0x3B5 => 0x395, +0x3B6 => 0x396, +0x3B7 => 0x397, +0x3B8 => 0x398, +0x3B9 => 0x399, +0x3BA => 0x39A, +0x3BB => 0x39B, +0x3BC => 0x39C, +0x3BD => 0x39D, +0x3BE => 0x39E, +0x3BF => 0x39F, +0x3C0 => 0x3A0, +0x3C1 => 0x3A1, +0x3C2 => 0x3A3, +0x3C3 => 0x3A3, +0x3C4 => 0x3A4, +0x3C5 => 0x3A5, +0x3C6 => 0x3A6, +0x3C7 => 0x3A7, +0x3C8 => 0x3A8, +0x3C9 => 0x3A9, +0x3CA => 0x3AA, +0x3CB => 0x3AB, +0x3CC => 0x38C, +0x3CD => 0x38E, +0x3CE => 0x38F, +0x3D0 => 0x392, +0x3D1 => 0x398, +0x3D5 => 0x3A6, +0x3D6 => 0x3A0, +0x3D7 => 0x3CF, +0x3D9 => 0x3D8, +0x3DB => 0x3DA, +0x3DD => 0x3DC, +0x3DF => 0x3DE, +0x3E1 => 0x3E0, +0x3E3 => 0x3E2, +0x3E5 => 0x3E4, +0x3E7 => 0x3E6, +0x3E9 => 0x3E8, +0x3EB => 0x3EA, +0x3ED => 0x3EC, +0x3EF => 0x3EE, +0x3F0 => 0x39A, +0x3F1 => 0x3A1, +0x3F2 => 0x3F9, +0x3F3 => 0x37F, +0x3F5 => 0x395, +0x3F8 => 0x3F7, +0x3FB => 0x3FA, +0x430 => 0x410, +0x431 => 0x411, +0x432 => 0x412, +0x433 => 0x413, +0x434 => 0x414, +0x435 => 0x415, +0x436 => 0x416, +0x437 => 0x417, +0x438 => 0x418, +0x439 => 0x419, +0x43A => 0x41A, +0x43B => 0x41B, +0x43C => 0x41C, +0x43D => 0x41D, +0x43E => 0x41E, +0x43F => 0x41F, +0x440 => 0x420, +0x441 => 0x421, +0x442 => 0x422, +0x443 => 0x423, +0x444 => 0x424, +0x445 => 0x425, +0x446 => 0x426, +0x447 => 0x427, +0x448 => 0x428, +0x449 => 0x429, +0x44A => 0x42A, +0x44B => 0x42B, +0x44C => 0x42C, +0x44D => 0x42D, +0x44E => 0x42E, +0x44F => 0x42F, +0x450 => 0x400, +0x451 => 0x401, +0x452 => 0x402, +0x453 => 0x403, +0x454 => 0x404, +0x455 => 0x405, +0x456 => 0x406, +0x457 => 0x407, +0x458 => 0x408, +0x459 => 0x409, +0x45A => 0x40A, +0x45B => 0x40B, +0x45C => 0x40C, +0x45D => 0x40D, +0x45E => 0x40E, +0x45F => 0x40F, +0x461 => 0x460, +0x463 => 0x462, +0x465 => 0x464, +0x467 => 0x466, +0x469 => 0x468, +0x46B => 0x46A, +0x46D => 0x46C, +0x46F => 0x46E, +0x471 => 0x470, +0x473 => 0x472, +0x475 => 0x474, +0x477 => 0x476, +0x479 => 0x478, +0x47B => 0x47A, +0x47D => 0x47C, +0x47F => 0x47E, +0x481 => 0x480, +0x48B => 0x48A, +0x48D => 0x48C, +0x48F => 0x48E, +0x491 => 0x490, +0x493 => 0x492, +0x495 => 0x494, +0x497 => 0x496, +0x499 => 0x498, +0x49B => 0x49A, +0x49D => 0x49C, +0x49F => 0x49E, +0x4A1 => 0x4A0, +0x4A3 => 0x4A2, +0x4A5 => 0x4A4, +0x4A7 => 0x4A6, +0x4A9 => 0x4A8, +0x4AB => 0x4AA, +0x4AD => 0x4AC, +0x4AF => 0x4AE, +0x4B1 => 0x4B0, +0x4B3 => 0x4B2, +0x4B5 => 0x4B4, +0x4B7 => 0x4B6, +0x4B9 => 0x4B8, +0x4BB => 0x4BA, +0x4BD => 0x4BC, +0x4BF => 0x4BE, +0x4C2 => 0x4C1, +0x4C4 => 0x4C3, +0x4C6 => 0x4C5, +0x4C8 => 0x4C7, +0x4CA => 0x4C9, +0x4CC => 0x4CB, +0x4CE => 0x4CD, +0x4CF => 0x4C0, +0x4D1 => 0x4D0, +0x4D3 => 0x4D2, +0x4D5 => 0x4D4, +0x4D7 => 0x4D6, +0x4D9 => 0x4D8, +0x4DB => 0x4DA, +0x4DD => 0x4DC, +0x4DF => 0x4DE, +0x4E1 => 0x4E0, +0x4E3 => 0x4E2, +0x4E5 => 0x4E4, +0x4E7 => 0x4E6, +0x4E9 => 0x4E8, +0x4EB => 0x4EA, +0x4ED => 0x4EC, +0x4EF => 0x4EE, +0x4F1 => 0x4F0, +0x4F3 => 0x4F2, +0x4F5 => 0x4F4, +0x4F7 => 0x4F6, +0x4F9 => 0x4F8, +0x4FB => 0x4FA, +0x4FD => 0x4FC, +0x4FF => 0x4FE, +0x501 => 0x500, +0x503 => 0x502, +0x505 => 0x504, +0x507 => 0x506, +0x509 => 0x508, +0x50B => 0x50A, +0x50D => 0x50C, +0x50F => 0x50E, +0x511 => 0x510, +0x513 => 0x512, +0x515 => 0x514, +0x517 => 0x516, +0x519 => 0x518, +0x51B => 0x51A, +0x51D => 0x51C, +0x51F => 0x51E, +0x521 => 0x520, +0x523 => 0x522, +0x525 => 0x524, +0x527 => 0x526, +0x529 => 0x528, +0x52B => 0x52A, +0x52D => 0x52C, +0x52F => 0x52E, +0x561 => 0x531, +0x562 => 0x532, +0x563 => 0x533, +0x564 => 0x534, +0x565 => 0x535, +0x566 => 0x536, +0x567 => 0x537, +0x568 => 0x538, +0x569 => 0x539, +0x56A => 0x53A, +0x56B => 0x53B, +0x56C => 0x53C, +0x56D => 0x53D, +0x56E => 0x53E, +0x56F => 0x53F, +0x570 => 0x540, +0x571 => 0x541, +0x572 => 0x542, +0x573 => 0x543, +0x574 => 0x544, +0x575 => 0x545, +0x576 => 0x546, +0x577 => 0x547, +0x578 => 0x548, +0x579 => 0x549, +0x57A => 0x54A, +0x57B => 0x54B, +0x57C => 0x54C, +0x57D => 0x54D, +0x57E => 0x54E, +0x57F => 0x54F, +0x580 => 0x550, +0x581 => 0x551, +0x582 => 0x552, +0x583 => 0x553, +0x584 => 0x554, +0x585 => 0x555, +0x586 => 0x556, +0x10D0 => 0x1C90, +0x10D1 => 0x1C91, +0x10D2 => 0x1C92, +0x10D3 => 0x1C93, +0x10D4 => 0x1C94, +0x10D5 => 0x1C95, +0x10D6 => 0x1C96, +0x10D7 => 0x1C97, +0x10D8 => 0x1C98, +0x10D9 => 0x1C99, +0x10DA => 0x1C9A, +0x10DB => 0x1C9B, +0x10DC => 0x1C9C, +0x10DD => 0x1C9D, +0x10DE => 0x1C9E, +0x10DF => 0x1C9F, +0x10E0 => 0x1CA0, +0x10E1 => 0x1CA1, +0x10E2 => 0x1CA2, +0x10E3 => 0x1CA3, +0x10E4 => 0x1CA4, +0x10E5 => 0x1CA5, +0x10E6 => 0x1CA6, +0x10E7 => 0x1CA7, +0x10E8 => 0x1CA8, +0x10E9 => 0x1CA9, +0x10EA => 0x1CAA, +0x10EB => 0x1CAB, +0x10EC => 0x1CAC, +0x10ED => 0x1CAD, +0x10EE => 0x1CAE, +0x10EF => 0x1CAF, +0x10F0 => 0x1CB0, +0x10F1 => 0x1CB1, +0x10F2 => 0x1CB2, +0x10F3 => 0x1CB3, +0x10F4 => 0x1CB4, +0x10F5 => 0x1CB5, +0x10F6 => 0x1CB6, +0x10F7 => 0x1CB7, +0x10F8 => 0x1CB8, +0x10F9 => 0x1CB9, +0x10FA => 0x1CBA, +0x10FD => 0x1CBD, +0x10FE => 0x1CBE, +0x10FF => 0x1CBF, +0x13F8 => 0x13F0, +0x13F9 => 0x13F1, +0x13FA => 0x13F2, +0x13FB => 0x13F3, +0x13FC => 0x13F4, +0x13FD => 0x13F5, +0x1C80 => 0x412, +0x1C81 => 0x414, +0x1C82 => 0x41E, +0x1C83 => 0x421, +0x1C84 => 0x422, +0x1C85 => 0x422, +0x1C86 => 0x42A, +0x1C87 => 0x462, +0x1C88 => 0xA64A, +0x1D79 => 0xA77D, +0x1D7D => 0x2C63, +0x1D8E => 0xA7C6, +0x1E01 => 0x1E00, +0x1E03 => 0x1E02, +0x1E05 => 0x1E04, +0x1E07 => 0x1E06, +0x1E09 => 0x1E08, +0x1E0B => 0x1E0A, +0x1E0D => 0x1E0C, +0x1E0F => 0x1E0E, +0x1E11 => 0x1E10, +0x1E13 => 0x1E12, +0x1E15 => 0x1E14, +0x1E17 => 0x1E16, +0x1E19 => 0x1E18, +0x1E1B => 0x1E1A, +0x1E1D => 0x1E1C, +0x1E1F => 0x1E1E, +0x1E21 => 0x1E20, +0x1E23 => 0x1E22, +0x1E25 => 0x1E24, +0x1E27 => 0x1E26, +0x1E29 => 0x1E28, +0x1E2B => 0x1E2A, +0x1E2D => 0x1E2C, +0x1E2F => 0x1E2E, +0x1E31 => 0x1E30, +0x1E33 => 0x1E32, +0x1E35 => 0x1E34, +0x1E37 => 0x1E36, +0x1E39 => 0x1E38, +0x1E3B => 0x1E3A, +0x1E3D => 0x1E3C, +0x1E3F => 0x1E3E, +0x1E41 => 0x1E40, +0x1E43 => 0x1E42, +0x1E45 => 0x1E44, +0x1E47 => 0x1E46, +0x1E49 => 0x1E48, +0x1E4B => 0x1E4A, +0x1E4D => 0x1E4C, +0x1E4F => 0x1E4E, +0x1E51 => 0x1E50, +0x1E53 => 0x1E52, +0x1E55 => 0x1E54, +0x1E57 => 0x1E56, +0x1E59 => 0x1E58, +0x1E5B => 0x1E5A, +0x1E5D => 0x1E5C, +0x1E5F => 0x1E5E, +0x1E61 => 0x1E60, +0x1E63 => 0x1E62, +0x1E65 => 0x1E64, +0x1E67 => 0x1E66, +0x1E69 => 0x1E68, +0x1E6B => 0x1E6A, +0x1E6D => 0x1E6C, +0x1E6F => 0x1E6E, +0x1E71 => 0x1E70, +0x1E73 => 0x1E72, +0x1E75 => 0x1E74, +0x1E77 => 0x1E76, +0x1E79 => 0x1E78, +0x1E7B => 0x1E7A, +0x1E7D => 0x1E7C, +0x1E7F => 0x1E7E, +0x1E81 => 0x1E80, +0x1E83 => 0x1E82, +0x1E85 => 0x1E84, +0x1E87 => 0x1E86, +0x1E89 => 0x1E88, +0x1E8B => 0x1E8A, +0x1E8D => 0x1E8C, +0x1E8F => 0x1E8E, +0x1E91 => 0x1E90, +0x1E93 => 0x1E92, +0x1E95 => 0x1E94, +0x1E9B => 0x1E60, +0x1EA1 => 0x1EA0, +0x1EA3 => 0x1EA2, +0x1EA5 => 0x1EA4, +0x1EA7 => 0x1EA6, +0x1EA9 => 0x1EA8, +0x1EAB => 0x1EAA, +0x1EAD => 0x1EAC, +0x1EAF => 0x1EAE, +0x1EB1 => 0x1EB0, +0x1EB3 => 0x1EB2, +0x1EB5 => 0x1EB4, +0x1EB7 => 0x1EB6, +0x1EB9 => 0x1EB8, +0x1EBB => 0x1EBA, +0x1EBD => 0x1EBC, +0x1EBF => 0x1EBE, +0x1EC1 => 0x1EC0, +0x1EC3 => 0x1EC2, +0x1EC5 => 0x1EC4, +0x1EC7 => 0x1EC6, +0x1EC9 => 0x1EC8, +0x1ECB => 0x1ECA, +0x1ECD => 0x1ECC, +0x1ECF => 0x1ECE, +0x1ED1 => 0x1ED0, +0x1ED3 => 0x1ED2, +0x1ED5 => 0x1ED4, +0x1ED7 => 0x1ED6, +0x1ED9 => 0x1ED8, +0x1EDB => 0x1EDA, +0x1EDD => 0x1EDC, +0x1EDF => 0x1EDE, +0x1EE1 => 0x1EE0, +0x1EE3 => 0x1EE2, +0x1EE5 => 0x1EE4, +0x1EE7 => 0x1EE6, +0x1EE9 => 0x1EE8, +0x1EEB => 0x1EEA, +0x1EED => 0x1EEC, +0x1EEF => 0x1EEE, +0x1EF1 => 0x1EF0, +0x1EF3 => 0x1EF2, +0x1EF5 => 0x1EF4, +0x1EF7 => 0x1EF6, +0x1EF9 => 0x1EF8, +0x1EFB => 0x1EFA, +0x1EFD => 0x1EFC, +0x1EFF => 0x1EFE, +0x1F00 => 0x1F08, +0x1F01 => 0x1F09, +0x1F02 => 0x1F0A, +0x1F03 => 0x1F0B, +0x1F04 => 0x1F0C, +0x1F05 => 0x1F0D, +0x1F06 => 0x1F0E, +0x1F07 => 0x1F0F, +0x1F10 => 0x1F18, +0x1F11 => 0x1F19, +0x1F12 => 0x1F1A, +0x1F13 => 0x1F1B, +0x1F14 => 0x1F1C, +0x1F15 => 0x1F1D, +0x1F20 => 0x1F28, +0x1F21 => 0x1F29, +0x1F22 => 0x1F2A, +0x1F23 => 0x1F2B, +0x1F24 => 0x1F2C, +0x1F25 => 0x1F2D, +0x1F26 => 0x1F2E, +0x1F27 => 0x1F2F, +0x1F30 => 0x1F38, +0x1F31 => 0x1F39, +0x1F32 => 0x1F3A, +0x1F33 => 0x1F3B, +0x1F34 => 0x1F3C, +0x1F35 => 0x1F3D, +0x1F36 => 0x1F3E, +0x1F37 => 0x1F3F, +0x1F40 => 0x1F48, +0x1F41 => 0x1F49, +0x1F42 => 0x1F4A, +0x1F43 => 0x1F4B, +0x1F44 => 0x1F4C, +0x1F45 => 0x1F4D, +0x1F51 => 0x1F59, +0x1F53 => 0x1F5B, +0x1F55 => 0x1F5D, +0x1F57 => 0x1F5F, +0x1F60 => 0x1F68, +0x1F61 => 0x1F69, +0x1F62 => 0x1F6A, +0x1F63 => 0x1F6B, +0x1F64 => 0x1F6C, +0x1F65 => 0x1F6D, +0x1F66 => 0x1F6E, +0x1F67 => 0x1F6F, +0x1F70 => 0x1FBA, +0x1F71 => 0x1FBB, +0x1F72 => 0x1FC8, +0x1F73 => 0x1FC9, +0x1F74 => 0x1FCA, +0x1F75 => 0x1FCB, +0x1F76 => 0x1FDA, +0x1F77 => 0x1FDB, +0x1F78 => 0x1FF8, +0x1F79 => 0x1FF9, +0x1F7A => 0x1FEA, +0x1F7B => 0x1FEB, +0x1F7C => 0x1FFA, +0x1F7D => 0x1FFB, +0x1F80 => 0x1F88, +0x1F81 => 0x1F89, +0x1F82 => 0x1F8A, +0x1F83 => 0x1F8B, +0x1F84 => 0x1F8C, +0x1F85 => 0x1F8D, +0x1F86 => 0x1F8E, +0x1F87 => 0x1F8F, +0x1F90 => 0x1F98, +0x1F91 => 0x1F99, +0x1F92 => 0x1F9A, +0x1F93 => 0x1F9B, +0x1F94 => 0x1F9C, +0x1F95 => 0x1F9D, +0x1F96 => 0x1F9E, +0x1F97 => 0x1F9F, +0x1FA0 => 0x1FA8, +0x1FA1 => 0x1FA9, +0x1FA2 => 0x1FAA, +0x1FA3 => 0x1FAB, +0x1FA4 => 0x1FAC, +0x1FA5 => 0x1FAD, +0x1FA6 => 0x1FAE, +0x1FA7 => 0x1FAF, +0x1FB0 => 0x1FB8, +0x1FB1 => 0x1FB9, +0x1FB3 => 0x1FBC, +0x1FBE => 0x399, +0x1FC3 => 0x1FCC, +0x1FD0 => 0x1FD8, +0x1FD1 => 0x1FD9, +0x1FE0 => 0x1FE8, +0x1FE1 => 0x1FE9, +0x1FE5 => 0x1FEC, +0x1FF3 => 0x1FFC, +0x214E => 0x2132, +0x2170 => 0x2160, +0x2171 => 0x2161, +0x2172 => 0x2162, +0x2173 => 0x2163, +0x2174 => 0x2164, +0x2175 => 0x2165, +0x2176 => 0x2166, +0x2177 => 0x2167, +0x2178 => 0x2168, +0x2179 => 0x2169, +0x217A => 0x216A, +0x217B => 0x216B, +0x217C => 0x216C, +0x217D => 0x216D, +0x217E => 0x216E, +0x217F => 0x216F, +0x2184 => 0x2183, +0x24D0 => 0x24B6, +0x24D1 => 0x24B7, +0x24D2 => 0x24B8, +0x24D3 => 0x24B9, +0x24D4 => 0x24BA, +0x24D5 => 0x24BB, +0x24D6 => 0x24BC, +0x24D7 => 0x24BD, +0x24D8 => 0x24BE, +0x24D9 => 0x24BF, +0x24DA => 0x24C0, +0x24DB => 0x24C1, +0x24DC => 0x24C2, +0x24DD => 0x24C3, +0x24DE => 0x24C4, +0x24DF => 0x24C5, +0x24E0 => 0x24C6, +0x24E1 => 0x24C7, +0x24E2 => 0x24C8, +0x24E3 => 0x24C9, +0x24E4 => 0x24CA, +0x24E5 => 0x24CB, +0x24E6 => 0x24CC, +0x24E7 => 0x24CD, +0x24E8 => 0x24CE, +0x24E9 => 0x24CF, +0x2C30 => 0x2C00, +0x2C31 => 0x2C01, +0x2C32 => 0x2C02, +0x2C33 => 0x2C03, +0x2C34 => 0x2C04, +0x2C35 => 0x2C05, +0x2C36 => 0x2C06, +0x2C37 => 0x2C07, +0x2C38 => 0x2C08, +0x2C39 => 0x2C09, +0x2C3A => 0x2C0A, +0x2C3B => 0x2C0B, +0x2C3C => 0x2C0C, +0x2C3D => 0x2C0D, +0x2C3E => 0x2C0E, +0x2C3F => 0x2C0F, +0x2C40 => 0x2C10, +0x2C41 => 0x2C11, +0x2C42 => 0x2C12, +0x2C43 => 0x2C13, +0x2C44 => 0x2C14, +0x2C45 => 0x2C15, +0x2C46 => 0x2C16, +0x2C47 => 0x2C17, +0x2C48 => 0x2C18, +0x2C49 => 0x2C19, +0x2C4A => 0x2C1A, +0x2C4B => 0x2C1B, +0x2C4C => 0x2C1C, +0x2C4D => 0x2C1D, +0x2C4E => 0x2C1E, +0x2C4F => 0x2C1F, +0x2C50 => 0x2C20, +0x2C51 => 0x2C21, +0x2C52 => 0x2C22, +0x2C53 => 0x2C23, +0x2C54 => 0x2C24, +0x2C55 => 0x2C25, +0x2C56 => 0x2C26, +0x2C57 => 0x2C27, +0x2C58 => 0x2C28, +0x2C59 => 0x2C29, +0x2C5A => 0x2C2A, +0x2C5B => 0x2C2B, +0x2C5C => 0x2C2C, +0x2C5D => 0x2C2D, +0x2C5E => 0x2C2E, +0x2C61 => 0x2C60, +0x2C65 => 0x23A, +0x2C66 => 0x23E, +0x2C68 => 0x2C67, +0x2C6A => 0x2C69, +0x2C6C => 0x2C6B, +0x2C73 => 0x2C72, +0x2C76 => 0x2C75, +0x2C81 => 0x2C80, +0x2C83 => 0x2C82, +0x2C85 => 0x2C84, +0x2C87 => 0x2C86, +0x2C89 => 0x2C88, +0x2C8B => 0x2C8A, +0x2C8D => 0x2C8C, +0x2C8F => 0x2C8E, +0x2C91 => 0x2C90, +0x2C93 => 0x2C92, +0x2C95 => 0x2C94, +0x2C97 => 0x2C96, +0x2C99 => 0x2C98, +0x2C9B => 0x2C9A, +0x2C9D => 0x2C9C, +0x2C9F => 0x2C9E, +0x2CA1 => 0x2CA0, +0x2CA3 => 0x2CA2, +0x2CA5 => 0x2CA4, +0x2CA7 => 0x2CA6, +0x2CA9 => 0x2CA8, +0x2CAB => 0x2CAA, +0x2CAD => 0x2CAC, +0x2CAF => 0x2CAE, +0x2CB1 => 0x2CB0, +0x2CB3 => 0x2CB2, +0x2CB5 => 0x2CB4, +0x2CB7 => 0x2CB6, +0x2CB9 => 0x2CB8, +0x2CBB => 0x2CBA, +0x2CBD => 0x2CBC, +0x2CBF => 0x2CBE, +0x2CC1 => 0x2CC0, +0x2CC3 => 0x2CC2, +0x2CC5 => 0x2CC4, +0x2CC7 => 0x2CC6, +0x2CC9 => 0x2CC8, +0x2CCB => 0x2CCA, +0x2CCD => 0x2CCC, +0x2CCF => 0x2CCE, +0x2CD1 => 0x2CD0, +0x2CD3 => 0x2CD2, +0x2CD5 => 0x2CD4, +0x2CD7 => 0x2CD6, +0x2CD9 => 0x2CD8, +0x2CDB => 0x2CDA, +0x2CDD => 0x2CDC, +0x2CDF => 0x2CDE, +0x2CE1 => 0x2CE0, +0x2CE3 => 0x2CE2, +0x2CEC => 0x2CEB, +0x2CEE => 0x2CED, +0x2CF3 => 0x2CF2, +0x2D00 => 0x10A0, +0x2D01 => 0x10A1, +0x2D02 => 0x10A2, +0x2D03 => 0x10A3, +0x2D04 => 0x10A4, +0x2D05 => 0x10A5, +0x2D06 => 0x10A6, +0x2D07 => 0x10A7, +0x2D08 => 0x10A8, +0x2D09 => 0x10A9, +0x2D0A => 0x10AA, +0x2D0B => 0x10AB, +0x2D0C => 0x10AC, +0x2D0D => 0x10AD, +0x2D0E => 0x10AE, +0x2D0F => 0x10AF, +0x2D10 => 0x10B0, +0x2D11 => 0x10B1, +0x2D12 => 0x10B2, +0x2D13 => 0x10B3, +0x2D14 => 0x10B4, +0x2D15 => 0x10B5, +0x2D16 => 0x10B6, +0x2D17 => 0x10B7, +0x2D18 => 0x10B8, +0x2D19 => 0x10B9, +0x2D1A => 0x10BA, +0x2D1B => 0x10BB, +0x2D1C => 0x10BC, +0x2D1D => 0x10BD, +0x2D1E => 0x10BE, +0x2D1F => 0x10BF, +0x2D20 => 0x10C0, +0x2D21 => 0x10C1, +0x2D22 => 0x10C2, +0x2D23 => 0x10C3, +0x2D24 => 0x10C4, +0x2D25 => 0x10C5, +0x2D27 => 0x10C7, +0x2D2D => 0x10CD, +0xA641 => 0xA640, +0xA643 => 0xA642, +0xA645 => 0xA644, +0xA647 => 0xA646, +0xA649 => 0xA648, +0xA64B => 0xA64A, +0xA64D => 0xA64C, +0xA64F => 0xA64E, +0xA651 => 0xA650, +0xA653 => 0xA652, +0xA655 => 0xA654, +0xA657 => 0xA656, +0xA659 => 0xA658, +0xA65B => 0xA65A, +0xA65D => 0xA65C, +0xA65F => 0xA65E, +0xA661 => 0xA660, +0xA663 => 0xA662, +0xA665 => 0xA664, +0xA667 => 0xA666, +0xA669 => 0xA668, +0xA66B => 0xA66A, +0xA66D => 0xA66C, +0xA681 => 0xA680, +0xA683 => 0xA682, +0xA685 => 0xA684, +0xA687 => 0xA686, +0xA689 => 0xA688, +0xA68B => 0xA68A, +0xA68D => 0xA68C, +0xA68F => 0xA68E, +0xA691 => 0xA690, +0xA693 => 0xA692, +0xA695 => 0xA694, +0xA697 => 0xA696, +0xA699 => 0xA698, +0xA69B => 0xA69A, +0xA723 => 0xA722, +0xA725 => 0xA724, +0xA727 => 0xA726, +0xA729 => 0xA728, +0xA72B => 0xA72A, +0xA72D => 0xA72C, +0xA72F => 0xA72E, +0xA733 => 0xA732, +0xA735 => 0xA734, +0xA737 => 0xA736, +0xA739 => 0xA738, +0xA73B => 0xA73A, +0xA73D => 0xA73C, +0xA73F => 0xA73E, +0xA741 => 0xA740, +0xA743 => 0xA742, +0xA745 => 0xA744, +0xA747 => 0xA746, +0xA749 => 0xA748, +0xA74B => 0xA74A, +0xA74D => 0xA74C, +0xA74F => 0xA74E, +0xA751 => 0xA750, +0xA753 => 0xA752, +0xA755 => 0xA754, +0xA757 => 0xA756, +0xA759 => 0xA758, +0xA75B => 0xA75A, +0xA75D => 0xA75C, +0xA75F => 0xA75E, +0xA761 => 0xA760, +0xA763 => 0xA762, +0xA765 => 0xA764, +0xA767 => 0xA766, +0xA769 => 0xA768, +0xA76B => 0xA76A, +0xA76D => 0xA76C, +0xA76F => 0xA76E, +0xA77A => 0xA779, +0xA77C => 0xA77B, +0xA77F => 0xA77E, +0xA781 => 0xA780, +0xA783 => 0xA782, +0xA785 => 0xA784, +0xA787 => 0xA786, +0xA78C => 0xA78B, +0xA791 => 0xA790, +0xA793 => 0xA792, +0xA794 => 0xA7C4, +0xA797 => 0xA796, +0xA799 => 0xA798, +0xA79B => 0xA79A, +0xA79D => 0xA79C, +0xA79F => 0xA79E, +0xA7A1 => 0xA7A0, +0xA7A3 => 0xA7A2, +0xA7A5 => 0xA7A4, +0xA7A7 => 0xA7A6, +0xA7A9 => 0xA7A8, +0xA7B5 => 0xA7B4, +0xA7B7 => 0xA7B6, +0xA7B9 => 0xA7B8, +0xA7BB => 0xA7BA, +0xA7BD => 0xA7BC, +0xA7BF => 0xA7BE, +0xA7C3 => 0xA7C2, +0xA7C8 => 0xA7C7, +0xA7CA => 0xA7C9, +0xA7F6 => 0xA7F5, +0xAB53 => 0xA7B3, +0xAB70 => 0x13A0, +0xAB71 => 0x13A1, +0xAB72 => 0x13A2, +0xAB73 => 0x13A3, +0xAB74 => 0x13A4, +0xAB75 => 0x13A5, +0xAB76 => 0x13A6, +0xAB77 => 0x13A7, +0xAB78 => 0x13A8, +0xAB79 => 0x13A9, +0xAB7A => 0x13AA, +0xAB7B => 0x13AB, +0xAB7C => 0x13AC, +0xAB7D => 0x13AD, +0xAB7E => 0x13AE, +0xAB7F => 0x13AF, +0xAB80 => 0x13B0, +0xAB81 => 0x13B1, +0xAB82 => 0x13B2, +0xAB83 => 0x13B3, +0xAB84 => 0x13B4, +0xAB85 => 0x13B5, +0xAB86 => 0x13B6, +0xAB87 => 0x13B7, +0xAB88 => 0x13B8, +0xAB89 => 0x13B9, +0xAB8A => 0x13BA, +0xAB8B => 0x13BB, +0xAB8C => 0x13BC, +0xAB8D => 0x13BD, +0xAB8E => 0x13BE, +0xAB8F => 0x13BF, +0xAB90 => 0x13C0, +0xAB91 => 0x13C1, +0xAB92 => 0x13C2, +0xAB93 => 0x13C3, +0xAB94 => 0x13C4, +0xAB95 => 0x13C5, +0xAB96 => 0x13C6, +0xAB97 => 0x13C7, +0xAB98 => 0x13C8, +0xAB99 => 0x13C9, +0xAB9A => 0x13CA, +0xAB9B => 0x13CB, +0xAB9C => 0x13CC, +0xAB9D => 0x13CD, +0xAB9E => 0x13CE, +0xAB9F => 0x13CF, +0xABA0 => 0x13D0, +0xABA1 => 0x13D1, +0xABA2 => 0x13D2, +0xABA3 => 0x13D3, +0xABA4 => 0x13D4, +0xABA5 => 0x13D5, +0xABA6 => 0x13D6, +0xABA7 => 0x13D7, +0xABA8 => 0x13D8, +0xABA9 => 0x13D9, +0xABAA => 0x13DA, +0xABAB => 0x13DB, +0xABAC => 0x13DC, +0xABAD => 0x13DD, +0xABAE => 0x13DE, +0xABAF => 0x13DF, +0xABB0 => 0x13E0, +0xABB1 => 0x13E1, +0xABB2 => 0x13E2, +0xABB3 => 0x13E3, +0xABB4 => 0x13E4, +0xABB5 => 0x13E5, +0xABB6 => 0x13E6, +0xABB7 => 0x13E7, +0xABB8 => 0x13E8, +0xABB9 => 0x13E9, +0xABBA => 0x13EA, +0xABBB => 0x13EB, +0xABBC => 0x13EC, +0xABBD => 0x13ED, +0xABBE => 0x13EE, +0xABBF => 0x13EF, +0xFF41 => 0xFF21, +0xFF42 => 0xFF22, +0xFF43 => 0xFF23, +0xFF44 => 0xFF24, +0xFF45 => 0xFF25, +0xFF46 => 0xFF26, +0xFF47 => 0xFF27, +0xFF48 => 0xFF28, +0xFF49 => 0xFF29, +0xFF4A => 0xFF2A, +0xFF4B => 0xFF2B, +0xFF4C => 0xFF2C, +0xFF4D => 0xFF2D, +0xFF4E => 0xFF2E, +0xFF4F => 0xFF2F, +0xFF50 => 0xFF30, +0xFF51 => 0xFF31, +0xFF52 => 0xFF32, +0xFF53 => 0xFF33, +0xFF54 => 0xFF34, +0xFF55 => 0xFF35, +0xFF56 => 0xFF36, +0xFF57 => 0xFF37, +0xFF58 => 0xFF38, +0xFF59 => 0xFF39, +0xFF5A => 0xFF3A, +0x10428 => 0x10400, +0x10429 => 0x10401, +0x1042A => 0x10402, +0x1042B => 0x10403, +0x1042C => 0x10404, +0x1042D => 0x10405, +0x1042E => 0x10406, +0x1042F => 0x10407, +0x10430 => 0x10408, +0x10431 => 0x10409, +0x10432 => 0x1040A, +0x10433 => 0x1040B, +0x10434 => 0x1040C, +0x10435 => 0x1040D, +0x10436 => 0x1040E, +0x10437 => 0x1040F, +0x10438 => 0x10410, +0x10439 => 0x10411, +0x1043A => 0x10412, +0x1043B => 0x10413, +0x1043C => 0x10414, +0x1043D => 0x10415, +0x1043E => 0x10416, +0x1043F => 0x10417, +0x10440 => 0x10418, +0x10441 => 0x10419, +0x10442 => 0x1041A, +0x10443 => 0x1041B, +0x10444 => 0x1041C, +0x10445 => 0x1041D, +0x10446 => 0x1041E, +0x10447 => 0x1041F, +0x10448 => 0x10420, +0x10449 => 0x10421, +0x1044A => 0x10422, +0x1044B => 0x10423, +0x1044C => 0x10424, +0x1044D => 0x10425, +0x1044E => 0x10426, +0x1044F => 0x10427, +0x104D8 => 0x104B0, +0x104D9 => 0x104B1, +0x104DA => 0x104B2, +0x104DB => 0x104B3, +0x104DC => 0x104B4, +0x104DD => 0x104B5, +0x104DE => 0x104B6, +0x104DF => 0x104B7, +0x104E0 => 0x104B8, +0x104E1 => 0x104B9, +0x104E2 => 0x104BA, +0x104E3 => 0x104BB, +0x104E4 => 0x104BC, +0x104E5 => 0x104BD, +0x104E6 => 0x104BE, +0x104E7 => 0x104BF, +0x104E8 => 0x104C0, +0x104E9 => 0x104C1, +0x104EA => 0x104C2, +0x104EB => 0x104C3, +0x104EC => 0x104C4, +0x104ED => 0x104C5, +0x104EE => 0x104C6, +0x104EF => 0x104C7, +0x104F0 => 0x104C8, +0x104F1 => 0x104C9, +0x104F2 => 0x104CA, +0x104F3 => 0x104CB, +0x104F4 => 0x104CC, +0x104F5 => 0x104CD, +0x104F6 => 0x104CE, +0x104F7 => 0x104CF, +0x104F8 => 0x104D0, +0x104F9 => 0x104D1, +0x104FA => 0x104D2, +0x104FB => 0x104D3, +0x10CC0 => 0x10C80, +0x10CC1 => 0x10C81, +0x10CC2 => 0x10C82, +0x10CC3 => 0x10C83, +0x10CC4 => 0x10C84, +0x10CC5 => 0x10C85, +0x10CC6 => 0x10C86, +0x10CC7 => 0x10C87, +0x10CC8 => 0x10C88, +0x10CC9 => 0x10C89, +0x10CCA => 0x10C8A, +0x10CCB => 0x10C8B, +0x10CCC => 0x10C8C, +0x10CCD => 0x10C8D, +0x10CCE => 0x10C8E, +0x10CCF => 0x10C8F, +0x10CD0 => 0x10C90, +0x10CD1 => 0x10C91, +0x10CD2 => 0x10C92, +0x10CD3 => 0x10C93, +0x10CD4 => 0x10C94, +0x10CD5 => 0x10C95, +0x10CD6 => 0x10C96, +0x10CD7 => 0x10C97, +0x10CD8 => 0x10C98, +0x10CD9 => 0x10C99, +0x10CDA => 0x10C9A, +0x10CDB => 0x10C9B, +0x10CDC => 0x10C9C, +0x10CDD => 0x10C9D, +0x10CDE => 0x10C9E, +0x10CDF => 0x10C9F, +0x10CE0 => 0x10CA0, +0x10CE1 => 0x10CA1, +0x10CE2 => 0x10CA2, +0x10CE3 => 0x10CA3, +0x10CE4 => 0x10CA4, +0x10CE5 => 0x10CA5, +0x10CE6 => 0x10CA6, +0x10CE7 => 0x10CA7, +0x10CE8 => 0x10CA8, +0x10CE9 => 0x10CA9, +0x10CEA => 0x10CAA, +0x10CEB => 0x10CAB, +0x10CEC => 0x10CAC, +0x10CED => 0x10CAD, +0x10CEE => 0x10CAE, +0x10CEF => 0x10CAF, +0x10CF0 => 0x10CB0, +0x10CF1 => 0x10CB1, +0x10CF2 => 0x10CB2, +0x118C0 => 0x118A0, +0x118C1 => 0x118A1, +0x118C2 => 0x118A2, +0x118C3 => 0x118A3, +0x118C4 => 0x118A4, +0x118C5 => 0x118A5, +0x118C6 => 0x118A6, +0x118C7 => 0x118A7, +0x118C8 => 0x118A8, +0x118C9 => 0x118A9, +0x118CA => 0x118AA, +0x118CB => 0x118AB, +0x118CC => 0x118AC, +0x118CD => 0x118AD, +0x118CE => 0x118AE, +0x118CF => 0x118AF, +0x118D0 => 0x118B0, +0x118D1 => 0x118B1, +0x118D2 => 0x118B2, +0x118D3 => 0x118B3, +0x118D4 => 0x118B4, +0x118D5 => 0x118B5, +0x118D6 => 0x118B6, +0x118D7 => 0x118B7, +0x118D8 => 0x118B8, +0x118D9 => 0x118B9, +0x118DA => 0x118BA, +0x118DB => 0x118BB, +0x118DC => 0x118BC, +0x118DD => 0x118BD, +0x118DE => 0x118BE, +0x118DF => 0x118BF, +0x16E60 => 0x16E40, +0x16E61 => 0x16E41, +0x16E62 => 0x16E42, +0x16E63 => 0x16E43, +0x16E64 => 0x16E44, +0x16E65 => 0x16E45, +0x16E66 => 0x16E46, +0x16E67 => 0x16E47, +0x16E68 => 0x16E48, +0x16E69 => 0x16E49, +0x16E6A => 0x16E4A, +0x16E6B => 0x16E4B, +0x16E6C => 0x16E4C, +0x16E6D => 0x16E4D, +0x16E6E => 0x16E4E, +0x16E6F => 0x16E4F, +0x16E70 => 0x16E50, +0x16E71 => 0x16E51, +0x16E72 => 0x16E52, +0x16E73 => 0x16E53, +0x16E74 => 0x16E54, +0x16E75 => 0x16E55, +0x16E76 => 0x16E56, +0x16E77 => 0x16E57, +0x16E78 => 0x16E58, +0x16E79 => 0x16E59, +0x16E7A => 0x16E5A, +0x16E7B => 0x16E5B, +0x16E7C => 0x16E5C, +0x16E7D => 0x16E5D, +0x16E7E => 0x16E5E, +0x16E7F => 0x16E5F, +0x1E922 => 0x1E900, +0x1E923 => 0x1E901, +0x1E924 => 0x1E902, +0x1E925 => 0x1E903, +0x1E926 => 0x1E904, +0x1E927 => 0x1E905, +0x1E928 => 0x1E906, +0x1E929 => 0x1E907, +0x1E92A => 0x1E908, +0x1E92B => 0x1E909, +0x1E92C => 0x1E90A, +0x1E92D => 0x1E90B, +0x1E92E => 0x1E90C, +0x1E92F => 0x1E90D, +0x1E930 => 0x1E90E, +0x1E931 => 0x1E90F, +0x1E932 => 0x1E910, +0x1E933 => 0x1E911, +0x1E934 => 0x1E912, +0x1E935 => 0x1E913, +0x1E936 => 0x1E914, +0x1E937 => 0x1E915, +0x1E938 => 0x1E916, +0x1E939 => 0x1E917, +0x1E93A => 0x1E918, +0x1E93B => 0x1E919, +0x1E93C => 0x1E91A, +0x1E93D => 0x1E91B, +0x1E93E => 0x1E91C, +0x1E93F => 0x1E91D, +0x1E940 => 0x1E91E, +0x1E941 => 0x1E91F, +0x1E942 => 0x1E920, +0x1E943 => 0x1E921, +]; diff --git a/vendor/opis/string/src/Exception/InvalidCodePointException.php b/vendor/opis/string/src/Exception/InvalidCodePointException.php new file mode 100644 index 000000000..9494adc64 --- /dev/null +++ b/vendor/opis/string/src/Exception/InvalidCodePointException.php @@ -0,0 +1,46 @@ +codePoint = $codePoint; + } + + /** + * @return mixed + */ + public function codePoint() + { + return$this->codePoint; + } +} diff --git a/vendor/opis/string/src/Exception/InvalidStringException.php b/vendor/opis/string/src/Exception/InvalidStringException.php new file mode 100644 index 000000000..50b22a188 --- /dev/null +++ b/vendor/opis/string/src/Exception/InvalidStringException.php @@ -0,0 +1,61 @@ +string = $string; + $this->offset = $offset; + } + + /** + * @return string + */ + public function string(): string + { + return $this->string; + } + + /** + * @return int + */ + public function offset(): int + { + return $this->offset; + } +} diff --git a/vendor/opis/string/src/Exception/UnicodeException.php b/vendor/opis/string/src/Exception/UnicodeException.php new file mode 100644 index 000000000..22e6d2faa --- /dev/null +++ b/vendor/opis/string/src/Exception/UnicodeException.php @@ -0,0 +1,25 @@ +codes = $codes; + $this->length = count($codes); + } + + /** + * @return int[] + */ + public function codePoints(): array + { + return $this->codes; + } + + /** + * @return string[] + */ + public function chars(): array + { + if ($this->chars === null) { + $this->chars = self::getCharsFromCodePoints($this->codes); + } + return $this->chars; + } + + /** + * @return int + */ + public function length(): int + { + return $this->length; + } + + /** + * @return bool + */ + public function isEmpty(): bool + { + return $this->length === 0; + } + + /** + * @param string|self|int[]|string[] $text + * @param bool $ignoreCase + * @return bool + */ + public function equals($text, bool $ignoreCase = false): bool + { + return $this->compareTo($text, $ignoreCase) === 0; + } + + /** + * @param string|self|int[]|string[] $text + * @param bool $ignoreCase + * @return int + */ + public function compareTo($text, bool $ignoreCase = false): int + { + $mode = $ignoreCase ? self::FOLD_CASE : self::KEEP_CASE; + + $text = self::resolveCodePoints($text, $mode); + + $length = count($text); + + if ($length !== $this->length) { + return $this->length <=> $length; + } + + return $this->getMappedCodes($mode) <=> $text; + } + + /** + * @param string|self|int[]|string[] $text + * @param bool $ignoreCase + * @return bool + */ + public function contains($text, bool $ignoreCase = false): bool + { + return $this->indexOf($text, 0, $ignoreCase) !== -1; + } + + /** + * @param string|self|int[]|string[] $text + * @param bool $ignoreCase + * @return bool + */ + public function startsWith($text, bool $ignoreCase = false): bool + { + $mode = $ignoreCase ? self::FOLD_CASE : self::KEEP_CASE; + + $text = self::resolveCodePoints($text, $mode); + + $len = count($text); + + if ($len === 0 || $len > $this->length) { + return false; + } + + return array_slice($this->getMappedCodes($mode), 0, $len) === $text; + } + + /** + * @param string|self|int[]|string[] $text + * @param bool $ignoreCase + * @return bool + */ + public function endsWith($text, bool $ignoreCase = false): bool + { + $mode = $ignoreCase ? self::FOLD_CASE : self::KEEP_CASE; + + $text = self::resolveCodePoints($text, $mode); + + if (empty($text)) { + return false; + } + + $codes = $this->getMappedCodes($mode); + + $offset = $this->length - count($text); + + if ($offset < 0) { + return false; + } + + return array_slice($codes, $offset) === $text; + } + + /** + * @param string|self|int[]|string[] $text + * @param int $offset + * @param bool $ignoreCase + * @return int + */ + public function indexOf($text, int $offset = 0, bool $ignoreCase = false): int + { + if ($offset < 0) { + $offset += $this->length; + } + if ($offset < 0 || $offset >= $this->length) { + return -1; + } + + $mode = $ignoreCase ? self::FOLD_CASE : self::KEEP_CASE; + + $text = self::resolveCodePoints($text, $mode); + + $len = count($text); + + if ($len === 0 || $offset + $len > $this->length) { + return -1; + } + + return $this->doIndexOf($this->getMappedCodes($mode), $text, $offset); + } + + /** + * @param string|self|int[]|string[] $text + * @param int $offset + * @param bool $ignoreCase + * @return int + */ + public function lastIndexOf($text, int $offset = 0, bool $ignoreCase = false): int + { + if ($offset < 0) { + $start = $this->length + $offset; + if ($start < 0) { + return -1; + } + $last = 0; + } else { + if ($offset >= $this->length) { + return -1; + } + $start = $this->length - 1; + $last = $offset; + } + + $mode = $ignoreCase ? self::FOLD_CASE : self::KEEP_CASE; + + $text = self::resolveCodePoints($text, $mode); + + $len = count($text); + + if ($len === 0) { + return -1; + } + + if ($offset < 0) { + if ($len > $this->length) { + return -1; + } + $start = min($start, $this->length - $len); + } elseif ($offset + $len > $this->length) { + return -1; + } + + $codes = $this->getMappedCodes($mode); + + for ($i = $start; $i >= $last; $i--) { + $match = true; + + for ($j = 0; $j < $len; $j++) { + if ($codes[$i + $j] !== $text[$j]) { + $match = false; + break; + } + } + + if ($match) { + return $i; + } + } + + return -1; + } + + /** + * @param string|self|int[]|string[] $text + * @param bool $ignoreCase + * @param bool $allowPrefixOnly If true the result can contain only the prefix + * @return $this + */ + public function ensurePrefix($text, bool $ignoreCase = false, bool $allowPrefixOnly = true): self + { + $text = self::resolveCodePoints($text); + + $len = count($text); + + if ($len === 0) { + return clone $this; + } + + if ($this->length === 0) { + return new static($text); + } + + if ($ignoreCase) { + $prefix = self::getMappedCodePoints($text, self::FOLD_CASE); + } else { + $prefix = &$text; + } + + if ($this->length === $len) { + $part = $this->getMappedCodes($ignoreCase ? self::FOLD_CASE : self::KEEP_CASE); + if ($allowPrefixOnly && $part === $prefix) { + return clone $this; + } + // Remove last element to avoid double check + array_pop($part); + } elseif ($this->length < $len) { + $part = $this->getMappedCodes($ignoreCase ? self::FOLD_CASE : self::KEEP_CASE); + // Checks if this can be a suffix + if ($allowPrefixOnly && (array_slice($prefix, 0, $this->length) === $part)) { + $text = array_slice($text, $this->length); + return new static(array_merge($this->codes, $text)); + } + } else { + $part = array_slice($this->codes, 0, $len); + if ($ignoreCase) { + $part = self::getMappedCodePoints($part, self::FOLD_CASE); + } + if ($part === $prefix) { + return clone $this; + } + // Remove last element to avoid double check + array_pop($part); + } + + $copy = $len; + + $part_len = count($part); + + while ($part_len) { + if ($part === array_slice($prefix, -$part_len)) { + $copy = $len - $part_len; + break; + } + array_pop($part); + $part_len--; + } + + if ($copy === 0) { + return clone $this; + } + + if ($copy < $len) { + $text = array_slice($text, 0, $copy); + } + + return new static(array_merge($text, $this->codes)); + } + + /** + * @param string|self|int[]|string[] $text + * @param bool $ignoreCase + * @param bool $allowSuffixOnly If true the result can contain only the suffix + * @return static + */ + public function ensureSuffix($text, bool $ignoreCase = false, bool $allowSuffixOnly = true): self + { + $text = self::resolveCodePoints($text); + + $len = count($text); + + if ($len === 0) { + return clone $this; + } + + if ($this->length === 0) { + return new static($text); + } + + if ($ignoreCase) { + $suffix = self::getMappedCodePoints($text, self::FOLD_CASE); + } else { + $suffix = &$text; + } + + if ($this->length === $len) { + $part = $this->getMappedCodes($ignoreCase ? self::FOLD_CASE : self::KEEP_CASE); + if ($allowSuffixOnly && $part === $suffix) { + return clone $this; + } + // Remove first element to avoid double check + array_shift($part); + } elseif ($this->length < $len) { + $part = $this->getMappedCodes($ignoreCase ? self::FOLD_CASE : self::KEEP_CASE); + // Checks if this can be a prefix + if ($allowSuffixOnly && (array_slice($suffix, -$this->length) === $part)) { + $text = array_slice($text, 0, $len - $this->length); + return new static(array_merge($text, $this->codes)); + } + } else { + $part = array_slice($this->codes, -$len); + if ($ignoreCase) { + $part = self::getMappedCodePoints($part, self::FOLD_CASE); + } + if ($part === $suffix) { + return clone $this; + } + // Remove first element to avoid double check + array_shift($part); + } + + $skip = 0; + + $part_len = count($part); + + while ($part_len) { + if ($part === array_slice($suffix, 0, $part_len)) { + $skip = $part_len; + break; + } + array_shift($part); + $part_len--; + } + + if ($skip === $len) { + return clone $this; + } + + if ($skip) { + array_splice($text, 0, $skip); + } + + return new static(array_merge($this->codes, $text)); + } + + /** + * @param string|self|int[]|string[] $text + * @param int $mode + * @return static + */ + public function append($text, int $mode = self::KEEP_CASE): self + { + return new static(array_merge($this->codes, self::resolveCodePoints($text, $mode))); + } + + /** + * @param string|self|int[]|string[] $text + * @param int $mode + * @return static + */ + public function prepend($text, int $mode = self::KEEP_CASE): self + { + return new static(array_merge(self::resolveCodePoints($text, $mode), $this->codes)); + } + + /** + * @param string|self|int[]|string[] $text + * @param int $offset + * @param int $mode + * @return static + */ + public function insert($text, int $offset, int $mode = self::KEEP_CASE): self + { + $codes = $this->codes; + + array_splice($codes, $offset, 0, self::resolveCodePoints($text, $mode)); + + return new static($codes); + } + + /** + * @param int $offset + * @param int|null $length + * @return static + */ + public function remove(int $offset, ?int $length = null): self + { + $codes = $this->codes; + + if ($length === null) { + array_splice($codes, $offset); + } else { + array_splice($codes, $offset, $length); + } + + return new static($codes); + } + + /** + * @param string|self|int[]|string[] $mask + * @return static + */ + public function trim($mask = " \t\n\r\0\x0B"): self + { + return $this->doTrim($mask, true, true); + } + + /** + * @param string|self|int[]|string[] $mask + * @return static + */ + public function trimLeft($mask = " \t\n\r\0\x0B"): self + { + return $this->doTrim($mask, true, false); + } + + /** + * @param string|self|int[]|string[] $mask + * @return static + */ + public function trimRight($mask = " \t\n\r\0\x0B"): self + { + return $this->doTrim($mask, false, true); + } + + /** + * @return static + */ + public function reverse(): self + { + return new static(array_reverse($this->codes)); + } + + /** + * @param int $times + * @return static + */ + public function repeat(int $times = 1): self + { + if ($times <= 1) { + return clone $this; + } + + $codes = []; + + while ($times--) { + $codes = array_merge($codes, $this->codes); + } + + return new static($codes); + } + + /** + * @param string|self|int[]|string[] $subject + * @param string|self|int[]|string[] $replace + * @param int $offset + * @param bool $ignoreCase + * @return static + */ + public function replace($subject, $replace, int $offset = 0, bool $ignoreCase = false): self + { + if ($offset < 0) { + $offset += $this->length; + } + if ($offset < 0 || $offset >= $this->length) { + return clone $this; + } + + $mode = $ignoreCase ? self::FOLD_CASE : self::KEEP_CASE; + + $subject = self::resolveCodePoints($subject, $mode); + + $len = count($subject); + + if ($len === 0 || $offset + $len > $this->length) { + return clone $this; + } + + $offset = $this->doIndexOf($this->getMappedCodes($mode), $subject, $offset); + + if ($offset === -1) { + return clone $this; + } + + $codes = $this->codes; + + array_splice($codes, $offset, count($subject), self::resolveCodePoints($replace)); + + return new static($codes); + } + + /** + * @param string|self|int[]|string[] $subject + * @param string|self|int[]|string[] $replace + * @param bool $ignoreCase + * @param int $offset + * @return static + */ + public function replaceAll($subject, $replace, int $offset = 0, bool $ignoreCase = false): self + { + if ($offset < 0) { + $offset += $this->length; + } + if ($offset < 0 || $offset >= $this->length) { + return clone $this; + } + + $mode = $ignoreCase ? self::FOLD_CASE : self::KEEP_CASE; + + $subject = self::resolveCodePoints($subject, $mode); + + $len = count($subject); + + if ($len === 0 || $offset + $len > $this->length) { + return clone $this; + } + + $replace = self::resolveCodePoints($replace); + + $codes = $this->getMappedCodes($mode); + + $copy = $this->codes; + + $fix = count($replace) - $len; + + $t = 0; + + while (($pos = $this->doIndexOf($codes, $subject, $offset)) >= 0) { + array_splice($copy, $pos + $t * $fix, $len, $replace); + $offset = $pos + $len; + $t++; + } + + return new static($copy); + } + + /** + * @param string|self|int[]|string[] $delimiter + * @param bool $ignoreCase + * @return array + */ + public function split($delimiter = '', bool $ignoreCase = false): array + { + $mode = $ignoreCase ? self::FOLD_CASE : self::KEEP_CASE; + $delimiter = self::resolveCodePoints($delimiter, $mode); + $len = count($delimiter); + + $ret = []; + + if ($len === 0) { + foreach ($this->codes as $code) { + $ret[] = new static([$code]); + } + } else { + $codes = $this->getMappedCodes($mode); + + $offset = 0; + + while (($pos = $this->doIndexOf($codes, $delimiter, $offset)) >= 0) { + $ret[] = new static(array_slice($this->codes, $offset, $pos - $offset)); + $offset = $pos + $len; + } + + $ret[] = new static(array_slice($this->codes, $offset)); + } + + return $ret; + } + + /** + * @param int $start + * @param int|null $length + * @return static + */ + public function substring(int $start, ?int $length = null): self + { + return new static(array_slice($this->codes, $start, $length)); + } + + /** + * @param int $size If negative then pad left otherwise pad right + * @param self|string|int $char A char or a code point + * @return static + */ + public function pad(int $size, $char = 0x20): self + { + return new static(array_pad($this->codes, $size, self::resolveFirstCodePoint($char, 0x20))); + } + + /** + * @param int $size + * @param self|string|int $char + * @return static + */ + public function padLeft(int $size, $char = 0x20): self + { + if ($size > 0) { + $size = -$size; + } + + return $this->pad($size, $char); + } + + /** + * @param int $size + * @param self|string|int $char + * @return static + */ + public function padRight(int $size, $char = 0x20): self + { + if ($size < 0) { + $size = -$size; + } + + return $this->pad($size, $char); + } + + /** + * @return bool + */ + public function isLowerCase(): bool + { + return $this->isCase(self::LOWER_CASE); + } + + /** + * @return bool + */ + public function isUpperCase(): bool + { + return $this->isCase(self::UPPER_CASE); + } + + /** + * @return bool + */ + public function isAscii(): bool + { + $key = 'i' . self::ASCII_CONV; + + if (!isset($this->cache[$key])) { + $ok = true; + + foreach ($this->codes as $code) { + if ($code >= 0x80) { + $ok = false; + break; + } + } + + $this->cache[$key] = $ok; + } + + return $this->cache[$key]; + } + + /** + * Convert all chars to lower case (where possible) + * @return static + */ + public function toLower(): self + { + if ($this->cache['i' . self::LOWER_CASE] ?? false) { + return clone $this; + } + return new static($this->getMappedCodes(self::LOWER_CASE)); + } + + /** + * Convert all chars to upper case (where possible) + * @return static + */ + public function toUpper(): self + { + if ($this->cache['i' . self::UPPER_CASE] ?? false) { + return clone $this; + } + return new static($this->getMappedCodes(self::UPPER_CASE)); + } + + /** + * Converts all chars to their ASCII equivalent (if any) + * @return static + */ + public function toAscii(): self + { + if ($this->cache['i' . self::ASCII_CONV] ?? false) { + return clone $this; + } + return new static($this->getMappedCodes(self::ASCII_CONV)); + } + + /** + * @param int $index + * @return string + */ + public function charAt(int $index): string + { + // Allow negative index + if ($index < 0 && $index + $this->length >= 0) { + $index += $this->length; + } + + if ($index < 0 || $index >= $this->length) { + return ''; + } + + return $this->chars()[$index]; + } + + /** + * @param int $index + * @return int + */ + public function codePointAt(int $index): int + { + // Allow negative index + if ($index < 0 && $index + $this->length >= 0) { + $index += $this->length; + } + + if ($index < 0 || $index >= $this->length) { + return -1; + } + + return $this->codes[$index]; + } + + /** + * @param int $offset + * @return int + */ + public function __invoke(int $offset): int + { + if ($offset < 0) { + if ($offset + $this->length < 0) { + throw new OutOfBoundsException("Undefined offset: {$offset}"); + } + $offset += $this->length; + } elseif ($offset >= $this->length) { + throw new OutOfBoundsException("Undefined offset: {$offset}"); + } + + return $this->codes[$offset]; + } + + /** + * @inheritDoc + */ + public function offsetExists($offset): bool + { + // Allow negative index + if ($offset < 0) { + $offset += $this->length; + } + + return isset($this->codes[$offset]); + } + + /** + * @inheritDoc + */ + public function offsetGet($offset): string + { + if ($offset < 0) { + if ($offset + $this->length < 0) { + throw new OutOfBoundsException("Undefined offset: {$offset}"); + } + $offset += $this->length; + } elseif ($offset >= $this->length) { + throw new OutOfBoundsException("Undefined offset: {$offset}"); + } + + return $this->chars()[$offset]; + } + + /** + * @inheritDoc + */ + #[\ReturnTypeWillChange] + public function offsetSet($offset, $value) + { + // Allow negative index + if ($offset < 0) { + $offset += $this->length; + } + + if (!isset($this->codes[$offset])) { + return; + } + + + $value = self::resolveFirstCodePoint($value); + if ($value === -1) { + return; + } + + if ($value === $this->codes[$offset]) { + // Same value, nothing to do + return; + } + + $this->codes[$offset] = $value; + + // Clear cache + $this->str = null; + $this->cache = null; + if ($this->chars) { + $this->chars[$offset] = self::getCharFromCodePoint($value); + } + } + + /** + * @inheritDoc + */ + #[\ReturnTypeWillChange] + public function offsetUnset($offset) + { + throw new RuntimeException("Invalid operation"); + } + + /** + * @inheritDoc + */ + public function count(): int + { + return $this->length; + } + + /** + * @return string + */ + public function __toString(): string + { + if ($this->str === null) { + $this->str = self::getStringFromCodePoints($this->codes); + } + + return $this->str; + } + + /** + * @inheritDoc + */ + public function jsonSerialize(): string + { + return $this->__toString(); + } + + public function __serialize(): array + { + return [ + 'value' => $this->__toString(), + ]; + } + + public function __unserialize(array $data): void + { + $this->str = $data['value']; + $this->codes = self::getCodePointsFromString($this->str); + $this->length = count($this->codes); + } + + /** + * Creates an unicode string instance from raw string + * @param string $string + * @param string|null $encoding Defaults to UTF-8 + * @param int $mode + * @return static + * @throws InvalidStringException + */ + public static function from(string $string, ?string $encoding = null, int $mode = self::KEEP_CASE): self + { + if ($encoding !== null && strcasecmp($encoding, 'UTF-8') !== 0) { + if (false === $string = @iconv($encoding, 'UTF-8', $string)) { + throw new UnicodeException("Could not convert string from '$encoding' encoding to UTF-8 encoding"); + } + } + + $instance = new static(self::getCodePointsFromString($string, $mode)); + if ($mode === self::KEEP_CASE) { + $instance->str = $string; + } + return $instance; + } + + /** + * Creates an unicode string instance from code points + * @param int[] $codes + * @param int $mode + * @return static + * @throws InvalidCodePointException + */ + public static function fromCodePoints(array $codes, int $mode = self::KEEP_CASE): self + { + $map = self::getMapByMode($mode); + + foreach ($codes as &$code) { + if (!is_int($codes) || !self::isValidCodePoint($code)) { + throw new InvalidCodePointException($code); + } else { + $code = $map[$code] ?? $code; + } + } + + return new static(array_values($codes)); + } + + /** + * Converts the code point to corresponding char + * @param int $code + * @return string The char or an empty string if code point is invalid + */ + public static function getCharFromCodePoint(int $code): string + { + if ($code < 0) { + return ''; + } + + if ($code < 0x80) { + return chr($code); + } + + if ($code < 0x800) { + return chr(($code >> 6) + 0xC0) . chr(($code & 0x3F) + 0x80); + } + + if ($code >= 0xD800 && $code <= 0xDFFF) { + /* + The definition of UTF-8 prohibits encoding character numbers between + U+D800 and U+DFFF, which are reserved for use with the UTF-16 + encoding form (as surrogate pairs) and do not directly represent characters. + */ + return ''; + } + + if ($code <= 0xFFFF) { + return + chr(($code >> 12) + 0xE0) . + chr((($code >> 6) & 0x3F) + 0x80) . + chr(($code & 0x3F) + 0x80); + } + + if ($code <= 0x10FFFF) { + return + chr(($code >> 18) + 0xF0) . + chr((($code >> 12) & 0x3F) + 0x80) . + chr((($code >> 6) & 0x3F) + 0x80) . + chr(($code & 0x3F) + 0x80); + } + + /* + Restricted the range of characters to 0000-10FFFF (the UTF-16 accessible range). + */ + + return ''; + } + + /** + * Convert a string to a code point array + * @param string $str + * @param int $mode + * @return array + * @throws InvalidStringException + */ + public static function getCodePointsFromString(string $str, int $mode = self::KEEP_CASE): array + { + // 0x00-0x7F + // 0xC2-0xDF 0x80-0xBF + // 0xE0-0xE0 0xA0-0xBF 0x80-0xBF + // 0xE1-0xEC 0x80-0xBF 0x80-0xBF + // 0xED-0xED 0x80-0x9F 0x80-0xBF + // 0xEE-0xEF 0x80-0xBF 0x80-0xBF + // 0xF0-0xF0 0x90-0xBF 0x80-0xBF 0x80-0xBF + // 0xF1-0xF3 0x80-0xBF 0x80-0xBF 0x80-0xBF + // 0xF4-0xF4 0x80-0x8F 0x80-0xBF 0x80-0xBF + + $codes = []; + $length = strlen($str); + $mode = self::getMapByMode($mode); + + $i = 0; + while ($i < $length) { + $ord0 = ord($str[$i++]); + + if ($ord0 < 0x80) { + $codes[] = $mode[$ord0] ?? $ord0; + continue; + } + + if ($i === $length || $ord0 < 0xC2 || $ord0 > 0xF4) { + throw new InvalidStringException($str, $i - 1); + } + + $ord1 = ord($str[$i++]); + + if ($ord0 < 0xE0) { + if ($ord1 < 0x80 || $ord1 >= 0xC0) { + throw new InvalidStringException($str, $i - 1); + } + + $ord1 = ($ord0 - 0xC0) * 64 + $ord1 - 0x80; + $codes[] = $mode[$ord1] ?? $ord1; + + continue; + } + + if ($i === $length) { + throw new InvalidStringException($str, $i - 1); + } + + $ord2 = ord($str[$i++]); + + if ($ord0 < 0xF0) { + if ($ord0 === 0xE0) { + if ($ord1 < 0xA0 || $ord1 >= 0xC0) { + throw new InvalidStringException($str, $i - 2); + } + } elseif ($ord0 === 0xED) { + if ($ord1 < 0x80 || $ord1 >= 0xA0) { + throw new InvalidStringException($str, $i - 2); + } + } elseif ($ord1 < 0x80 || $ord1 >= 0xC0) { + throw new InvalidStringException($str, $i - 2); + } + + if ($ord2 < 0x80 || $ord2 >= 0xC0) { + throw new InvalidStringException($str, $i - 1); + } + + $ord2 = ($ord0 - 0xE0) * 0x1000 + ($ord1 - 0x80) * 64 + $ord2 - 0x80; + $codes[] = $mode[$ord2] ?? $ord2; + + continue; + } + + if ($i === $length) { + throw new InvalidStringException($str, $i - 1); + } + + $ord3 = ord($str[$i++]); + + if ($ord0 < 0xF5) { + if ($ord0 === 0xF0) { + if ($ord1 < 0x90 || $ord1 >= 0xC0) { + throw new InvalidStringException($str, $i - 3); + } + } elseif ($ord0 === 0xF4) { + if ($ord1 < 0x80 || $ord1 >= 0x90) { + throw new InvalidStringException($str, $i - 3); + } + } elseif ($ord1 < 0x80 || $ord1 >= 0xC0) { + throw new InvalidStringException($str, $i - 3); + } + + if ($ord2 < 0x80 || $ord2 >= 0xC0) { + throw new InvalidStringException($str, $i - 2); + } + + if ($ord3 < 0x80 || $ord3 >= 0xC0) { + throw new InvalidStringException($str, $i - 1); + } + + $ord3 = ($ord0 - 0xF0) * 0x40000 + ($ord1 - 0x80) * 0x1000 + ($ord2 - 0x80) * 64 + $ord3 - 0x80; + $codes[] = $mode[$ord3] ?? $ord3; + + continue; + } + + throw new InvalidStringException($str, $i - 1); + } + + return $codes; + } + + /** + * @param string $str + * @return iterable + * + * The key represents the current char index + * Value is a two element array + * - first element is an integer representing the code point + * - second element is an array of integers (length 1 to 4) representing bytes + */ + public static function walkString(string $str): iterable + { + $i = 0; + $length = strlen($str); + + while ($i < $length) { + $index = $i; + + $ord0 = ord($str[$i++]); + + if ($ord0 < 0x80) { + yield $index => [ + $ord0, + [$ord0] + ]; + continue; + } + + if ($i === $length || $ord0 < 0xC2 || $ord0 > 0xF4) { + throw new InvalidStringException($str, $i - 1); + } + + $ord1 = ord($str[$i++]); + + if ($ord0 < 0xE0) { + if ($ord1 < 0x80 || $ord1 >= 0xC0) { + throw new InvalidStringException($str, $i - 1); + } + + yield $index => [ + ($ord0 - 0xC0) * 64 + $ord1 - 0x80, + [$ord0, $ord1] + ]; + + continue; + } + + if ($i === $length) { + throw new InvalidStringException($str, $i - 1); + } + + $ord2 = ord($str[$i++]); + + if ($ord0 < 0xF0) { + if ($ord0 === 0xE0) { + if ($ord1 < 0xA0 || $ord1 >= 0xC0) { + throw new InvalidStringException($str, $i - 2); + } + } elseif ($ord0 === 0xED) { + if ($ord1 < 0x80 || $ord1 >= 0xA0) { + throw new InvalidStringException($str, $i - 2); + } + } elseif ($ord1 < 0x80 || $ord1 >= 0xC0) { + throw new InvalidStringException($str, $i - 2); + } + + if ($ord2 < 0x80 || $ord2 >= 0xC0) { + throw new InvalidStringException($str, $i - 1); + } + + yield $index => [ + ($ord0 - 0xE0) * 0x1000 + ($ord1 - 0x80) * 64 + $ord2 - 0x80, + [$ord0, $ord1, $ord2] + ]; + + continue; + } + + if ($i === $length) { + throw new InvalidStringException($str, $i - 1); + } + + $ord3 = ord($str[$i++]); + + if ($ord0 < 0xF5) { + if ($ord0 === 0xF0) { + if ($ord1 < 0x90 || $ord1 >= 0xC0) { + throw new InvalidStringException($str, $i - 3); + } + } elseif ($ord0 === 0xF4) { + if ($ord1 < 0x80 || $ord1 >= 0x90) { + throw new InvalidStringException($str, $i - 3); + } + } elseif ($ord1 < 0x80 || $ord1 >= 0xC0) { + throw new InvalidStringException($str, $i - 3); + } + + if ($ord2 < 0x80 || $ord2 >= 0xC0) { + throw new InvalidStringException($str, $i - 2); + } + + if ($ord3 < 0x80 || $ord3 >= 0xC0) { + throw new InvalidStringException($str, $i - 1); + } + + yield $index => [ + ($ord0 - 0xF0) * 0x40000 + ($ord1 - 0x80) * 0x1000 + ($ord2 - 0x80) * 64 + $ord3 - 0x80, + [$ord0, $ord1, $ord2, $ord3] + ]; + + continue; + } + + throw new InvalidStringException($str, $i - 1); + } + } + + /** + * Converts each code point to a char + * @param array $codes + * @param int $mode + * @return array + * @throws InvalidCodePointException + */ + public static function getCharsFromCodePoints(array $codes, int $mode = self::KEEP_CASE): array + { + $mode = self::getMapByMode($mode); + + foreach ($codes as &$code) { + $char = self::getCharFromCodePoint($mode[$code] ?? $code); + if ($char === '') { + throw new InvalidCodePointException($code); + } else { + $code = $char; + } + } + + return $codes; + } + + /** + * @param string $str + * @param int $mode + * @return string[] + */ + public static function getCharsFromString(string $str, int $mode = self::KEEP_CASE): array + { + return self::getCharsFromCodePoints(self::getCodePointsFromString($str), $mode); + } + + /** + * Converts all code points to chars and returns the string + * Invalid code points are ignored + * @param array $codes + * @param int $mode + * @return string + */ + public static function getStringFromCodePoints(array $codes, int $mode = self::KEEP_CASE): string + { + $str = ''; + + $mode = self::getMapByMode($mode); + + foreach ($codes as $code) { + if (isset($mode[$code])) { + $code = $mode[$code]; + } + + if ($code < 0x80) { + $str .= chr($code); + continue; + } + + if ($code < 0x800) { + $str .= chr(($code >> 6) + 0xC0) . chr(($code & 0x3F) + 0x80); + continue; + } + + if ($code >= 0xD800 && $code <= 0xDFFF) { + continue; + } + + if ($code <= 0xFFFF) { + $str .= + chr(($code >> 12) + 0xE0) . + chr((($code >> 6) & 0x3F) + 0x80) . + chr(($code & 0x3F) + 0x80); + continue; + } + + if ($code <= 0x10FFFF) { + $str .= + chr(($code >> 18) + 0xF0) . + chr((($code >> 12) & 0x3F) + 0x80) . + chr((($code >> 6) & 0x3F) + 0x80) . + chr(($code & 0x3F) + 0x80); + } + } + + return $str; + } + + /** + * @param array $codes + * @param int $mode + * @return array + */ + public static function getMappedCodePoints(array $codes, int $mode): array + { + if ($mode === self::KEEP_CASE) { + return $codes; + } + + $mode = self::getMapByMode($mode); + + if (empty($mode)) { + return $codes; + } + + foreach ($codes as &$code) { + $code = $mode[$code] ?? $code; + } + + return $codes; + } + + /** + * Checks if a code point is valid + * @param int $code + * @return bool + */ + public static function isValidCodePoint(int $code): bool + { + if ($code < 0 || $code > 0x10FFFF) { + return false; + } + + return $code < 0xD800 || $code > 0xDFFF; + } + + /** + * @param int $mode + * @return int[] + */ + private function getMappedCodes(int $mode): array + { + if ($mode === self::KEEP_CASE || ($this->cache['i' . $mode] ?? false)) { + return $this->codes; + } + + $key = 'm' . $mode; + + if (!isset($this->cache[$key])) { + $this->cache[$key] = self::getMappedCodePoints($this->codes, $mode); + } + + return $this->cache[$key]; + } + + /** + * @param int $mode + * @return bool + */ + private function isCase(int $mode): bool + { + $key = 'i' . $mode; + + if (!isset($this->cache[$key])) { + $list = self::getMapByMode($mode); + foreach ($this->codes as $code) { + if (isset($list[$code])) { + return $this->cache[$key] = false; + } + } + + return $this->cache[$key] = true; + } + + return $this->cache[$key]; + } + + /** + * @param int[] $codes + * @param int[] $text + * @param int $offset + * @return int + */ + private function doIndexOf(array $codes, array $text, int $offset = 0): int + { + $len = count($text); + + for ($i = $offset, $last = count($codes) - $len; $i <= $last; $i++) { + $match = true; + + for ($j = 0; $j < $len; $j++) { + if ($codes[$i + $j] !== $text[$j]) { + $match = false; + break; + } + } + + if ($match) { + return $i; + } + } + + return -1; + } + + /** + * @param string|self|int[]|string[] $mask + * @param bool $left + * @param bool $right + * @return static + */ + private function doTrim($mask, bool $left, bool $right): self + { + if ($this->length === 0) { + return clone $this; + } + + $mask = self::resolveCodePoints($mask); + + if (empty($mask)) { + return clone $this; + } + + $codes = $this->codes; + + if ($left) { + while (in_array($codes[0], $mask, true)) { + array_shift($codes); + if (empty($codes)) { + return new static(); + } + } + } + + if ($right) { + $last = count($codes) - 1; + while (in_array($codes[$last], $mask, true)) { + array_pop($codes); + if (--$last < 0) { + return new static(); + } + } + } + + return new static($codes); + } + + + /** + * @param string|self|int[]|string[] $text + * @param int $mode + * @return array + */ + private static function resolveCodePoints($text, int $mode = self::KEEP_CASE): array + { + if ($text instanceof self) { + return $text->getMappedCodes($mode); + } + + if (is_string($text)) { + return self::getCodePointsFromString($text, $mode); + } + + if ($text && is_array($text) && is_int($text[0])) { + // assume code point array + return self::getMappedCodePoints($text, $mode); + } + + return []; + } + + /** + * @param self|string|int|string[]|int[] $text + * @param int $invalid + * @return int + */ + private static function resolveFirstCodePoint($text, int $invalid = -1): int + { + if ($text instanceof self) { + return $text->length === 0 ? $invalid : $text->codes[0]; + } + + if (is_array($text)) { + if (empty($text)) { + return $invalid; + } + $text = reset($text); + } + + if (is_string($text)) { + if (isset($text[4])) { + $text = substr($text, 0, 4); + } + return self::getCodePointsFromString($text)[0] ?? $invalid; + } + + if (is_int($text)) { + return self::isValidCodePoint($text) ? $text : $invalid; + } + + return $invalid; + } + + /** + * @param int $mode + * @return int[] + */ + private static function getMapByMode(int $mode): array + { + if (isset(self::$maps[$mode])) { + return self::$maps[$mode]; + } + + switch ($mode) { + case self::LOWER_CASE: + $file = 'lower'; + break; + case self::UPPER_CASE: + $file = 'upper'; + break; + case self::ASCII_CONV: + $file = 'ascii'; + break; + case self::FOLD_CASE: + $file = 'fold'; + break; + default: + return []; + } + + /** @noinspection PhpIncludeInspection */ + return self::$maps[$mode] = include(__DIR__ . "/../res/{$file}.php"); + } +} diff --git a/vendor/opis/uri/LICENSE b/vendor/opis/uri/LICENSE new file mode 100644 index 000000000..d9a10c0d8 --- /dev/null +++ b/vendor/opis/uri/LICENSE @@ -0,0 +1,176 @@ + 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 diff --git a/vendor/opis/uri/README.md b/vendor/opis/uri/README.md new file mode 100644 index 000000000..9d82dca9a --- /dev/null +++ b/vendor/opis/uri/README.md @@ -0,0 +1,40 @@ +Opis URI +========== +[![Tests](https://github.com/opis/uri/workflows/Tests/badge.svg)](https://github.com/opis/uri/actions) +[![Latest Stable Version](https://poser.pugx.org/opis/uri/version.png)](https://packagist.org/packages/opis/uri) +[![Latest Unstable Version](https://poser.pugx.org/opis/uri/v/unstable.png)](//packagist.org/packages/opis/uri) +[![License](https://poser.pugx.org/opis/uri/license.png)](https://packagist.org/packages/opis/uri) + + +**Opis URI** library allows you to build, parse and validate URIs and URI-templates. + +## License + +**Opis URI** is licensed under the [Apache License, Version 2.0][license]. + +## Requirements + +* PHP ^7.4 || ^8.0 + +## Installation + +**Opis URI** is available on [Packagist] and it can be installed from a +command line interface by using [Composer]. + +```bash +composer require opis/uri +``` + +Or you could directly reference it into your `composer.json` file as a dependency + +```json +{ + "require": { + "opis/uri": "^1.0" + } +} +``` + +[license]: https://www.apache.org/licenses/LICENSE-2.0 "Apache License" +[Packagist]: https://packagist.org/packages/opis/database "Packagist" +[Composer]: https://getcomposer.org "Composer" \ No newline at end of file diff --git a/vendor/opis/uri/autoload.php b/vendor/opis/uri/autoload.php new file mode 100644 index 000000000..8aa754c5e --- /dev/null +++ b/vendor/opis/uri/autoload.php @@ -0,0 +1,42 @@ += $n && $input[$i] < $m) { + $m = $input[$i]; + } + } + + if (($m - $n) > intdiv(self::MAX_INT - $delta, $handled + 1)) { + throw new PunycodeException("Punycode overflow"); + } + + $delta += ($m - $n) * ($handled + 1); + + $n = $m; + + for ($i = 0; $i < $input_len; $i++) { + if ($input[$i] < $n && (++$delta === 0)) { + throw new PunycodeException("Punycode overflow"); + } + + if ($input[$i] === $n) { + $q = $delta; + for ($k = self::BASE; ; $k += self::BASE) { + $t = self::threshold($k, $bias); + if ($q < $t) { + break; + } + + $base_minus_t = self::BASE - $t; + + $q -= $t; + + $output[] = self::encodeDigit($t + ($q % $base_minus_t)); + + $q = intdiv($q, $base_minus_t); + } + + $output[] = self::encodeDigit($q); + + $bias = self::adapt($delta, $handled + 1, $handled === $basic_length); + $delta = 0; + $handled++; + } + } + + $delta++; $n++; + } + + return self::PREFIX . UnicodeString::getStringFromCodePoints($output); + } + + public static function decodePart(string $input): string + { + if (stripos($input, self::PREFIX) !== 0) { + return $input; + } + + $input = UnicodeString::getCodePointsFromString(substr($input, self::PREFIX_LEN), UnicodeString::LOWER_CASE); + $input_len = count($input); + + $pos = array_keys($input, self::DELIMITER, true); + if ($pos) { + $pos = end($pos); + } else { + $pos = -1; + } + + /** @var int $pos */ + + if ($pos === -1) { + $output = []; + $pos = $output_len = 0; + } else { + $output = array_slice($input, 0, ++$pos); + $output_len = $pos; + for ($i = 0; $i < $pos; $i++) { + if ($output[$i] >= 0x80) { + throw new PunycodeException("Non-basic code point is not allowed: {$output[$i]}"); + } + } + } + + $i = 0; + $n = self::INITIAL_N; + $bias = self::INITIAL_BIAS; + + while ($pos < $input_len) { + $old_i = $i; + + for ($w = 1, $k = self::BASE; ; $k += self::BASE) { + if ($pos >= $input_len) { + throw new PunycodeException("Punycode bad input"); + } + + $digit = self::decodeDigit($input[$pos++]); + + if ($digit >= self::BASE || $digit > intdiv(self::MAX_INT - $i, $w)) { + throw new PunycodeException("Punycode overflow"); + } + + $i += $digit * $w; + + $t = self::threshold($k, $bias); + if ($digit < $t) { + break; + } + + $t = self::BASE - $t; + + if ($w > intdiv(self::MAX_INT, $t)) { + throw new PunycodeException("Punycode overflow"); + } + + $w *= $t; + } + + $output_len++; + + if (intdiv($i, $output_len) > self::MAX_INT - $n) { + throw new PunycodeException("Punycode overflow"); + } + + $n += intdiv($i, $output_len); + + $bias = self::adapt($i - $old_i, $output_len, $old_i === 0); + + $i %= $output_len; + + array_splice($output, $i, 0, $n); + + $i++; + } + + return UnicodeString::getStringFromCodePoints($output); + } + + public static function normalizePart(string $input): string + { + $input = strtolower($input); + + if (strpos($input, self::DELIMITER) === 0) { + self::decodePart($input); // just validate + return $input; + } + + return self::encodePart($input); + } + + private static function encodeDigit(int $digit): int + { + return $digit + 0x16 + ($digit < 0x1A ? 0x4B: 0x00); + } + + private static function decodeDigit(int $code): int + { + if ($code < 0x3A) { + return $code - 0x16; + } + if ($code < 0x5B) { + return $code - 0x41; + } + if ($code < 0x7B) { + return $code - 0x61; + } + + return self::BASE; + } + + private static function threshold(int $k, int $bias): int + { + $d = $k - $bias; + + if ($d <= self::TMIN) { + return self::TMIN; + } + + if ($d >= self::TMAX) { + return self::TMAX; + } + + return $d; + } + + private static function adapt(int $delta, int $num_points, bool $first_time = false): int + { + $delta = intdiv($delta, $first_time ? self::DAMP : 2); + $delta += intdiv($delta, $num_points); + + $k = 0; + $base_tmin_diff = self::BASE - self::TMIN; + $lim = $base_tmin_diff * self::TMAX / 2; + + while ($delta > $lim) { + $delta = intdiv($delta, $base_tmin_diff); + $k += self::BASE; + } + + $k += intdiv(($base_tmin_diff + 1) * $delta, $delta + self::SKEW); + + return $k; + } +} \ No newline at end of file diff --git a/vendor/opis/uri/src/PunycodeException.php b/vendor/opis/uri/src/PunycodeException.php new file mode 100644 index 000000000..6eb1c1291 --- /dev/null +++ b/vendor/opis/uri/src/PunycodeException.php @@ -0,0 +1,25 @@ +[^:]+)(?::(?.*))?$`'; + + protected const HOST_LABEL_REGEX = '`^(?:(?:%[a-f0-9]{2})+|[a-z0-9-]+)*$`i'; + + protected const AUTHORITY_REGEX = '`^(?:(?[^@]+)\@)?(?(\[[a-f0-9:]+\]|[^:]+))(?::(?\d+))?$`i'; + + protected const PATH_REGEX = '`^(?:(?:%[a-f0-9]{2})+|[a-z0-9-._~!$&\'()*+,;=:@/]+)*$`i'; + + protected const QUERY_OR_FRAGMENT_REGEX = '`^(?:(?:%[a-f0-9]{2})+|[a-z0-9-._~!$&\'"()\[\]*+,;=:@?/%]+)*$`i'; + + protected array $components; + + protected ?string $str = null; + + /** + * @param array $components An array of normalized components + */ + public function __construct(array $components) + { + $this->components = $components + [ + 'scheme' => null, + 'user' => null, + 'pass' => null, + 'host' => null, + 'port' => null, + 'path' => null, + 'query' => null, + 'fragment' => null, + ]; + } + + /** + * @return string|null + */ + public function scheme(): ?string + { + return $this->components['scheme']; + } + + /** + * @return string|null + */ + public function user(): ?string + { + return $this->components['user']; + } + + /** + * @return string|null + */ + public function pass(): ?string + { + return $this->components['pass']; + } + + /** + * @return string|null + */ + public function userInfo(): ?string + { + if ($this->components['user'] === null) { + return null; + } + + if ($this->components['pass'] === null) { + return $this->components['user']; + } + + return $this->components['user'] . ':' . $this->components['pass']; + } + + /** + * @return string|null + */ + public function host(): ?string + { + return $this->components['host']; + } + + /** + * @return int|null + */ + public function port(): ?int + { + return $this->components['port']; + } + + /** + * @return string|null + */ + public function authority(): ?string + { + if ($this->components['host'] === null) { + return null; + } + + $authority = $this->userInfo(); + if ($authority !== null) { + $authority .= '@'; + } + + $authority .= $this->components['host']; + + if ($this->components['port'] !== null) { + $authority .= ':' . $this->components['port']; + } + + return $authority; + } + + /** + * @return string|null + */ + public function path(): ?string + { + return $this->components['path']; + } + + /** + * @return string|null + */ + public function query(): ?string + { + return $this->components['query']; + } + + /** + * @return string|null + */ + public function fragment(): ?string + { + return $this->components['fragment']; + } + + /** + * @return array|null[] + */ + public function components(): array + { + return $this->components; + } + + /** + * @return bool + */ + public function isAbsolute(): bool + { + return $this->components['scheme'] !== null; + } + + /** + * Use this URI as base to resolve the reference + * @param static|string|array $ref + * @param bool $normalize + * @return $this|null + */ + public function resolveRef($ref, bool $normalize = false): ?self + { + $ref = self::resolveComponents($ref); + if ($ref === null) { + return $this; + } + + return new static(self::mergeComponents($ref, $this->components, $normalize)); + } + + /** + * Resolve this URI reference using a base URI + * @param static|string|array $base + * @param bool $normalize + * @return static + */ + public function resolve($base, bool $normalize = false): self + { + if ($this->isAbsolute()) { + return $this; + } + + $base = self::resolveComponents($base); + + if ($base === null) { + return $this; + } + + return new static(self::mergeComponents($this->components, $base, $normalize)); + } + + /** + * @return string + */ + public function __toString(): string + { + if ($this->str !== null) { + return $this->str; + } + + $str = ''; + + if ($this->components['scheme'] !== null) { + $str .= $this->components['scheme'] . ':'; + } + + if ($this->components['host'] !== null) { + $str .= '//' . $this->authority(); + } + + $str .= $this->components['path']; + + if ($this->components['query'] !== null) { + $str .= '?' . $this->components['query']; + } + + if ($this->components['fragment'] !== null) { + $str .= '#' . $this->components['fragment']; + } + + return $this->str = $str; + } + + /** + * @param string $uri + * @param bool $normalize + * @return static|null + */ + public static function create(string $uri, bool $normalize = false): ?self + { + $comp = self::parseComponents($uri); + if (!$comp) { + return null; + } + + if ($normalize) { + $comp = self::normalizeComponents($comp); + } + + return new static($comp); + } + + /** + * Checks if the scheme contains valid chars + * @param string $scheme + * @return bool + */ + public static function isValidScheme(string $scheme): bool + { + return (bool)preg_match(self::SCHEME_REGEX, $scheme); + } + + /** + * Checks if user contains valid chars + * @param string $user + * @return bool + */ + public static function isValidUser(string $user): bool + { + return (bool)preg_match(self::USER_OR_PASS_REGEX, $user); + } + + /** + * Checks if pass contains valid chars + * @param string $pass + * @return bool + */ + public static function isValidPass(string $pass): bool + { + return (bool)preg_match(self::USER_OR_PASS_REGEX, $pass); + } + + /** + * @param string $userInfo + * @return bool + */ + public static function isValidUserInfo(string $userInfo): bool + { + /** @var array|string $userInfo */ + + if (!preg_match(self::USERINFO_REGEX, $userInfo, $userInfo)) { + return false; + } + + if (!self::isValidUser($userInfo['user'])) { + return false; + } + + if (isset($userInfo['pass'])) { + return self::isValidPass($userInfo['pass']); + } + + return true; + } + + /** + * Checks if host is valid + * @param string $host + * @return bool + */ + public static function isValidHost(string $host): bool + { + // min and max length + if ($host === '' || isset($host[253])) { + return false; + } + + // check ipv6 + if ($host[0] === '[') { + if ($host[-1] !== ']') { + return false; + } + + return filter_var( + substr($host, 1, -1), + \FILTER_VALIDATE_IP, + \FILTER_FLAG_IPV6 + ) !== false; + } + + // check ipv4 + if (preg_match('`^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\$`', $host)) { + return \filter_var($host, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4) !== false; + } + + foreach (explode('.', $host) as $host) { + // empty or too long label + if ($host === '' || isset($host[63])) { + return false; + } + if ($host[0] === '-' || $host[-1] === '-') { + return false; + } + if (!preg_match(self::HOST_LABEL_REGEX, $host)) { + return false; + } + } + + return true; + } + + /** + * Checks if the port is valid + * @param int $port + * @return bool + */ + public static function isValidPort(int $port): bool + { + return $port >= 0 && $port <= 65535; + } + + /** + * Checks if authority contains valid chars + * @param string $authority + * @return bool + */ + public static function isValidAuthority(string $authority): bool + { + if ($authority === '') { + return true; + } + + /** @var array|string $authority */ + + if (!preg_match(self::AUTHORITY_REGEX, $authority, $authority)) { + return false; + } + + if (isset($authority['port']) && !self::isValidPort((int)$authority['port'])) { + return false; + } + + if (isset($authority['userinfo']) && !self::isValidUserInfo($authority['userinfo'])) { + return false; + } + + return self::isValidHost($authority['host']); + } + + /** + * Checks if the path contains valid chars + * @param string $path + * @return bool + */ + public static function isValidPath(string $path): bool + { + return $path === '' || (bool)preg_match(self::PATH_REGEX, $path); + } + + /** + * Checks if the query string contains valid chars + * @param string $query + * @return bool + */ + public static function isValidQuery(string $query): bool + { + return $query === '' || (bool)preg_match(self::QUERY_OR_FRAGMENT_REGEX, $query); + } + + /** + * Checks if the fragment contains valid chars + * @param string $fragment + * @return bool + */ + public static function isValidFragment(string $fragment): bool + { + return $fragment === '' || (bool)preg_match(self::QUERY_OR_FRAGMENT_REGEX, $fragment); + } + + /** + * @param string $uri + * @param bool $expand_authority + * @param bool $validate + * @return array|null + */ + public static function parseComponents(string $uri, bool $expand_authority = true, bool $validate = true): ?array + { + if (!preg_match(self::URI_REGEX, $uri, $uri)) { + return null; + } + + $comp = []; + + // scheme + if (isset($uri[2]) && $uri[2] !== '') { + if ($validate && !self::isValidScheme($uri[2])) { + return null; + } + $comp['scheme'] = $uri[2]; + } + + // authority + if (isset($uri[4]) && isset($uri[3][0])) { + if ($uri[4] === '') { + if ($expand_authority) { + $comp['host'] = ''; + } else { + $comp['authority'] = ''; + } + } elseif ($expand_authority) { + $au = self::parseAuthorityComponents($uri[4], $validate); + if ($au === null) { + return null; + } + $comp += $au; + unset($au); + } else { + if ($validate && !self::isValidAuthority($uri[4])) { + return null; + } + $comp['authority'] = $uri[4]; + } + } + + // path + if (isset($uri[5])) { + if ($validate && !self::isValidPath($uri[5])) { + return null; + } + $comp['path'] = $uri[5]; + // not a relative uri, remove dot segments + if (isset($comp['scheme']) || isset($comp['authority']) || isset($comp['host'])) { + $comp['path'] = self::removeDotSegmentsFromPath($comp['path']); + } + } + + // query + if (isset($uri[7]) && isset($uri[6][0])) { + if ($validate && !self::isValidQuery($uri[7])) { + return null; + } + $comp['query'] = $uri[7]; + } + + // fragment + if (isset($uri[9]) && isset($uri[8][0])) { + if ($validate && !self::isValidFragment($uri[9])) { + return null; + } + $comp['fragment'] = $uri[9]; + } + + return $comp; + } + + /** + * @param self|string|array $uri + * @return array|null + */ + public static function resolveComponents($uri): ?array + { + if ($uri instanceof self) { + return $uri->components; + } + + if (is_string($uri)) { + return self::parseComponents($uri); + } + + if (is_array($uri)) { + if (isset($uri['host'])) { + unset($uri['authority']); + } elseif (isset($uri['authority'])) { + $au = self::parseAuthorityComponents($uri['authority']); + unset($uri['authority']); + if ($au !== null) { + unset($uri['user'], $uri['pass'], $uri['host'], $uri['port']); + $uri += $au; + } + } + return $uri; + } + + return null; + } + + /** + * @param string $authority + * @param bool $validate + * @return array|null + */ + public static function parseAuthorityComponents(string $authority, bool $validate = true): ?array + { + /** @var array|string $authority */ + + if (!preg_match(self::AUTHORITY_REGEX, $authority, $authority)) { + return null; + } + + $comp = []; + + // userinfo + if (isset($authority['userinfo']) && $authority['userinfo'] !== '') { + if (!preg_match(self::USERINFO_REGEX, $authority['userinfo'], $ui)) { + return null; + } + + // user + if ($validate && !self::isValidUser($ui['user'])) { + return null; + } + $comp['user'] = $ui['user']; + + // pass + if (isset($ui['pass']) && $ui['pass'] !== '') { + if ($validate && !self::isValidPass($ui['pass'])) { + return null; + } + $comp['pass'] = $ui['pass']; + } + + unset($ui); + } + + // host + if ($validate && !self::isValidHost($authority['host'])) { + return null; + } + $comp['host'] = $authority['host']; + + + // port + if (isset($authority['port'])) { + $authority['port'] = (int)$authority['port']; + if (!self::isValidPort($authority['port'])) { + return null; + } + $comp['port'] = $authority['port']; + } + + return $comp; + } + + /** + * @param array $ref + * @param array $base + * @param bool $normalize + * @return array + */ + public static function mergeComponents(array $ref, array $base, bool $normalize = false): array + { + if (isset($ref['scheme'])) { + $dest = $ref; + } else { + $dest = []; + + $dest['scheme'] = $base['scheme'] ?? null; + + if (isset($ref['authority']) || isset($ref['host'])) { + $dest += $ref; + } else { + if (isset($base['authority'])) { + $dest['authority'] = $base['authority']; + } else { + $dest['user'] = $base['user'] ?? null; + $dest['pass'] = $base['pass'] ?? null; + $dest['host'] = $base['host'] ?? null; + $dest['port'] = $base['port'] ?? null; + } + + if (!isset($ref['path'])) { + $ref['path'] = ''; + } + if (!isset($base['path'])) { + $base['path'] = ''; + } + + if ($ref['path'] === '') { + $dest['path'] = $base['path']; + $dest['query'] = $ref['query'] ?? $base['query'] ?? null; + } else { + if ($ref['path'][0] === '/') { + $dest['path'] = $ref['path']; + } else { + if ((isset($base['authority']) || isset($base['host'])) && $base['path'] === '') { + $dest['path'] = '/' . $ref['path']; + } else { + $dest['path'] = $base['path']; + + if ($dest['path'] !== '') { + $pos = strrpos($dest['path'], '/'); + if ($pos === false) { + $dest['path'] = ''; + } else { + $dest['path'] = substr($dest['path'], 0, $pos); + } + + unset($pos); + } + $dest['path'] .= '/' . $ref['path']; + } + } + + $dest['query'] = $ref['query'] ?? null; + } + } + } + + $dest['fragment'] = $ref['fragment'] ?? null; + + if ($normalize) { + return self::normalizeComponents($dest); + } + + if (isset($dest['path'])) { + $dest['path'] = self::removeDotSegmentsFromPath($dest['path']); + } + + return $dest; + } + + public static function normalizeComponents(array $components): array + { + if (isset($components['scheme'])) { + $components['scheme'] = strtolower($components['scheme']); + // Remove default port + if (isset($components['port']) && self::getSchemePort($components['scheme']) === $components['port']) { + $components['port'] = null; + } + } + + if (isset($components['host'])) { + $components['host'] = strtolower($components['host']); + } + + if (isset($components['path'])) { + $components['path'] = self::removeDotSegmentsFromPath($components['path']); + } + + if (isset($components['query'])) { + $components['query'] = self::normalizeQueryString($components['query']); + } + + return $components; + } + + /** + * Removes dot segments from path + * @param string $path + * @return string + */ + public static function removeDotSegmentsFromPath(string $path): string + { + // Fast check common simple paths + if ($path === '' || $path === '/') { + return $path; + } + + $output = ''; + $last_slash = 0; + + $len = strlen($path); + $i = 0; + + while ($i < $len) { + if ($path[$i] === '.') { + $j = $i + 1; + // search for . + if ($j >= $len) { + break; + } + + // search for ./ + if ($path[$j] === '/') { + $i = $j + 1; + continue; + } + + // search for ../ + if ($path[$j] === '.') { + $k = $j + 1; + if ($k >= $len) { + break; + } + if ($path[$k] === '/') { + $i = $k + 1; + continue; + } + } + } elseif ($path[$i] === '/') { + $j = $i + 1; + if ($j >= $len) { + $output .= '/'; + break; + } + + // search for /. + if ($path[$j] === '.') { + $k = $j + 1; + if ($k >= $len) { + $output .= '/'; + break; + } + // search for /./ + if ($path[$k] === '/') { + $i = $k; + continue; + } + // search for /.. + if ($path[$k] === '.') { + $n = $k + 1; + if ($n >= $len) { + // keep the slash + $output = substr($output, 0, $last_slash + 1); + break; + } + // search for /../ + if ($path[$n] === '/') { + $output = substr($output, 0, $last_slash); + $last_slash = (int)strrpos($output, '/'); + $i = $n; + continue; + } + } + } + } + + $pos = strpos($path, '/', $i + 1); + + if ($pos === false) { + $output .= substr($path, $i); + break; + } + + $last_slash = strlen($output); + $output .= substr($path, $i, $pos - $i); + + $i = $pos; + } + + return $output; + } + + /** + * @param string|null $query + * @return array + */ + public static function parseQueryString(?string $query): array + { + if ($query === null) { + return []; + } + + $list = []; + + foreach (explode('&', $query) as $name) { + $value = null; + if (($pos = strpos($name, '=')) !== false) { + $value = self::decodeComponent(substr($name, $pos + 1)); + $name = self::decodeComponent(substr($name, 0, $pos)); + } else { + $name = self::decodeComponent($name); + } + $list[$name] = $value; + } + + return $list; + } + + /** + * @param array $qs + * @param string|null $prefix + * @param string $separator + * @param bool $sort + * @return string + */ + public static function buildQueryString(array $qs, ?string $prefix = null, + string $separator = '&', bool $sort = false): string + { + $isIndexed = static function (array $array): bool { + for ($i = 0, $max = count($array); $i < $max; $i++) { + if (!array_key_exists($i, $array)) { + return false; + } + } + return true; + }; + + $f = static function (array $arr, ?string $prefix = null) use (&$f, &$isIndexed): iterable { + $indexed = $prefix !== null && $isIndexed($arr); + + foreach ($arr as $key => $value) { + if ($prefix !== null) { + $key = $prefix . ($indexed ? "[]" : "[{$key}]"); + } + if (is_array($value)) { + yield from $f($value, $key); + } else { + yield $key => $value; + } + } + }; + + $data = []; + + foreach ($f($qs, $prefix) as $key => $value) { + $item = is_string($key) ? self::encodeComponent($key) : $key; + if ($value !== null) { + $item .= '='; + $item .= is_string($value) ? self::encodeComponent($value) : $value; + } + if ($item === '' || $item === '=') { + continue; + } + $data[] = $item; + } + + if (!$data) { + return ''; + } + + if ($sort) { + sort($data); + } + + return implode($separator, $data); + } + + /** + * @param string $query + * @return string + */ + public static function normalizeQueryString(string $query): string + { + return static::buildQueryString(self::parseQueryString($query), null, '&', true); + } + + public static function decodeComponent(string $component): string + { + return rawurldecode($component); + } + + public static function encodeComponent(string $component, ?array $skip = null): string + { + if (!$skip) { + return rawurlencode($component); + } + + $str = ''; + + foreach (UnicodeString::walkString($component) as [$cp, $chars]) { + if ($cp < 0x80) { + if ($cp === 0x2D || $cp === 0x2E || + $cp === 0x5F || $cp === 0x7E || + ($cp >= 0x41 && $cp <= 0x5A) || + ($cp >= 0x61 && $cp <= 0x7A) || + ($cp >= 0x30 && $cp <= 0x39) || + in_array($cp, $skip, true) + ) { + $str .= chr($cp); + } else { + $str .= '%' . strtoupper(dechex($cp)); + } + } else { + $i = 0; + while (isset($chars[$i])) { + $str .= '%' . strtoupper(dechex($chars[$i++])); + } + } + } + + return $str; + } + + public static function setSchemePort(string $scheme, ?int $port): void + { + $scheme = strtolower($scheme); + + if ($port === null) { + unset(self::$KNOWN_PORTS[$scheme]); + } else { + self::$KNOWN_PORTS[$scheme] = $port; + } + } + + public static function getSchemePort(string $scheme): ?int + { + return self::$KNOWN_PORTS[strtolower($scheme)] ?? null; + } + + protected static array $KNOWN_PORTS = [ + 'ftp' => 21, + 'ssh' => 22, + 'telnet' => 23, + 'smtp' => 25, + 'tftp' => 69, + 'http' => 80, + 'pop' => 110, + 'sftp' => 115, + 'imap' => 143, + 'irc' => 194, + 'ldap' => 389, + 'https' => 443, + 'ldaps' => 636, + 'telnets' => 992, + 'imaps' => 993, + 'ircs' => 994, + 'pops' => 995, + ]; +} \ No newline at end of file diff --git a/vendor/opis/uri/src/UriTemplate.php b/vendor/opis/uri/src/UriTemplate.php new file mode 100644 index 000000000..98c5b693b --- /dev/null +++ b/vendor/opis/uri/src/UriTemplate.php @@ -0,0 +1,520 @@ +[a-zA-Z0-9\_\%\.]+)(?:(?\*)?|\:(?\d+))?$~'; + + /** @var string */ + protected const TEMPLATE_REGEX = <<<'REGEX' +~\{ +(?[+#./;&=,!@|\?])? +(? + (?:(?P>varspec),)* + (?(?: + [a-zA-Z0-9\_\%\.]+ + (?:\*|\:\d+)? + )) +) +\}~x +REGEX; + + /** @var array */ + protected const TEMPLATE_TABLE = [ + '' => [ + 'first' => '', + 'sep' => ',', + 'named' => false, + 'ifemp' => '', + 'allow' => false, + ], + '+' => [ + 'first' => '', + 'sep' => ',', + 'named' => false, + 'ifemp' => '', + 'allow' => true, + ], + '.' => [ + 'first' => '.', + 'sep' => '.', + 'named' => false, + 'ifemp' => '', + 'allow' => false, + ], + '/' => [ + 'first' => '/', + 'sep' => '/', + 'named' => false, + 'ifemp' => '', + 'allow' => false, + ], + ';' => [ + 'first' => ';', + 'sep' => ';', + 'named' => true, + 'ifemp' => '', + 'allow' => false, + ], + '?' => [ + 'first' => '?', + 'sep' => '&', + 'named' => true, + 'ifemp' => '=', + 'allow' => false, + ], + '&' => [ + 'first' => '&', + 'sep' => '&', + 'named' => true, + 'ifemp' => '=', + 'allow' => false, + ], + '#' => [ + 'first' => '#', + 'sep' => ',', + 'named' => false, + 'ifemp' => '', + 'allow' => true, + ], + ]; + + protected string $uri; + + /** @var bool|null|array */ + protected $parsed = false; + + /** + * UriTemplate constructor. + * @param string $uri_template + */ + public function __construct(string $uri_template) + { + $this->uri = $uri_template; + } + + /** + * @param array $vars + * @return string + */ + public function resolve(array $vars): string + { + if ($this->parsed === false) { + $this->parsed = $this->parse($this->uri); + } + if ($this->parsed === null || !$vars) { + return $this->uri; + } + + $data = ''; + $vars = $this->prepareVars($vars); + + foreach ($this->parsed as $item) { + if (!is_array($item)) { + $data .= $item; + continue; + } + + $data .= $this->parseTemplateExpression( + self::TEMPLATE_TABLE[$item['operator']], + $this->resolveVars($item['vars'], $vars) + ); + } + + return $data; + } + + /** + * @return bool + */ + public function hasPlaceholders(): bool + { + if ($this->parsed === false) { + $this->parse($this->uri); + } + + return $this->parsed !== null; + } + + /** + * @param string $uri + * @return array|null + */ + protected function parse(string $uri): ?array + { + $placeholders = null; + preg_match_all(self::TEMPLATE_REGEX, $uri, $placeholders, PREG_SET_ORDER | PREG_OFFSET_CAPTURE); + + if (!$placeholders) { + return null; + } + + $dataIndex = -1; + $data = []; + + $hasVars = false; + $nextOffset = 0; + foreach ($placeholders as &$p) { + $offset = $p[0][1]; + if ($nextOffset < $offset) { + $data[] = substr($uri, $nextOffset, $offset - $nextOffset); + $dataIndex++; + } + $matched = $p[0][0]; + $nextOffset = $offset + strlen($matched); + + $operator = $p['operator'][0] ?? null; + if ($operator === null || !isset(self::TEMPLATE_TABLE[$operator])) { + if ($dataIndex >= 0 && is_string($data[$dataIndex])) { + $data[$dataIndex] .= $matched; + } else { + $data[] = $matched; + $dataIndex++; + } + continue; + } + + $varList = $p['varlist'][0] ?? ''; + $varList = $varList === '' ? [] : explode(',', $varList); + $p = null; + + $varData = []; + + foreach ($varList as $var) { + if (!preg_match(self::TEMPLATE_VARSPEC_REGEX, $var, $spec)) { + continue; + } + + $varData[] = [ + 'name' => $spec['varname'], + 'explode' => isset($spec['explode']) && $spec['explode'] === '*', + 'prefix' => isset($spec['prefix']) ? (int)$spec['prefix'] : 0, + ]; + + unset($var, $spec); + } + + if ($varData) { + $hasVars = true; + $data[] = [ + 'operator' => $operator, + 'vars' => $varData, + ]; + $dataIndex++; + } else { + if ($dataIndex >= 0 && is_string($data[$dataIndex])) { + $data[$dataIndex] .= $matched; + } else { + $data[] = $matched; + $dataIndex++; + } + } + + unset($varData, $varList, $operator); + } + + if (!$hasVars) { + return null; + } + + $matched = substr($uri, $nextOffset); + if ($matched !== false && $matched !== '') { + if ($dataIndex >= 0 && is_string($data[$dataIndex])) { + $data[$dataIndex] .= $matched; + } else { + $data[] = $matched; + } + } + + return $data; + } + + /** + * Convert assoc arrays to objects + * @param array $vars + * @return array + */ + protected function prepareVars(array $vars): array + { + foreach ($vars as &$value) { + if (is_scalar($value)) { + if (!is_string($value)) { + $value = (string)$value; + } + continue; + } + + if (!is_array($value)) { + continue; + } + + $len = count($value); + for ($i = 0; $i < $len; $i++) { + if (!array_key_exists($i, $value)) { + $value = (object)$value; + break; + } + } + } + + return $vars; + } + + /** + * @param array $vars + * @param array $data + * @return array + */ + protected function resolveVars(array $vars, array $data): array + { + $resolved = []; + + foreach ($vars as $info) { + $name = $info['name']; + + if (!isset($data[$name])) { + continue; + } + + $resolved[] = $info + ['value' => &$data[$name]]; + } + + return $resolved; + } + + /** + * @param array $table + * @param array $data + * @return string + */ + protected function parseTemplateExpression(array $table, array $data): string + { + $result = []; + foreach ($data as $var) { + $str = ""; + if (is_string($var['value'])) { + if ($table['named']) { + $str .= $var['name']; + if ($var['value'] === '') { + $str .= $table['ifemp']; + } else { + $str .= '='; + } + } + if ($var['prefix']) { + $str .= $this->encodeTemplateString(self::prefix($var['value'], $var['prefix']), $table['allow']); + } else { + $str .= $this->encodeTemplateString($var['value'], $table['allow']); + } + } elseif ($var['explode']) { + $list = []; + if ($table['named']) { + if (is_array($var['value'])) { + foreach ($var['value'] as $v) { + if (is_null($v) || !is_scalar($v)) { + continue; + } + $v = $this->encodeTemplateString((string)$v, $table['allow']); + if ($v === '') { + $list[] = $var['name'] . $table['ifemp']; + } else { + $list[] = $var['name'] . '=' . $v; + } + } + } elseif (is_object($var['value'])) { + foreach ($var['value'] as $prop => $v) { + if (is_null($v) || !is_scalar($v)) { + continue; + } + $v = $this->encodeTemplateString((string)$v, $table['allow']); + $prop = $this->encodeTemplateString((string)$prop, $table['allow']); + if ($v === '') { + $list[] = $prop . $table['ifemp']; + } else { + $list[] = $prop . '=' . $v; + } + } + } + } else { + if (is_array($var['value'])) { + foreach ($var['value'] as $v) { + if (is_null($v) || !is_scalar($v)) { + continue; + } + $list[] = $this->encodeTemplateString($v, $table['allow']); + } + } elseif (is_object($var['value'])) { + foreach ($var['value'] as $prop => $v) { + if (is_null($v) || !is_scalar($v)) { + continue; + } + $v = $this->encodeTemplateString((string)$v, $table['allow']); + $prop = $this->encodeTemplateString((string)$prop, $table['allow']); + $list[] = $prop . '=' . $v; + } + } + } + + if ($list) { + $str .= implode($table['sep'], $list); + } + unset($list); + } else { + if ($table['named']) { + $str .= $var['name']; + if ($var['value'] === '') { + $str .= $table['ifemp']; + } else { + $str .= '='; + } + } + $list = []; + if (is_array($var['value'])) { + foreach ($var['value'] as $v) { + $list[] = $this->encodeTemplateString($v, $table['allow']); + } + } elseif (is_object($var['value'])) { + foreach ($var['value'] as $prop => $v) { + $list[] = $this->encodeTemplateString((string)$prop, $table['allow']); + $list[] = $this->encodeTemplateString((string)$v, $table['allow']); + } + } + if ($list) { + $str .= implode(',', $list); + } + unset($list); + } + + if ($str !== '') { + $result[] = $str; + } + } + + if (!$result) { + return ''; + } + + $result = implode($table['sep'], $result); + + if ($result !== '') { + $result = $table['first'] . $result; + } + + return $result; + } + + /** + * @param string $data + * @param bool $reserved + * @return string + */ + protected function encodeTemplateString(string $data, bool $reserved): string + { + $skip = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~'; + + if ($reserved) { + $skip .= ':/?#[]@!$&\'()*+,;='; + } + + $result = ''; + $temp = ''; + for ($i = 0, $len = strlen($data); $i < $len; $i++) { + if (strpos($skip, $data[$i]) !== false) { + if ($temp !== '') { + $result .= Uri::encodeComponent($temp); + $temp = ''; + } + $result .= $data[$i]; + continue; + } + if ($reserved && $data[$i] === '%') { + if (isset($data[$i + 1]) && isset($data[$i + 2]) + && strpos('ABCDEF0123456789', $data[$i + 1]) !== false + && strpos('ABCDEF0123456789', $data[$i + 2]) !== false) { + if ($temp !== '') { + $result .= Uri::encodeComponent($temp); + } + $result .= '%' . $data[$i + 1] . $data[$i + 2]; + $i += 3; + continue; + } + } + $temp .= $data[$i]; + } + + if ($temp !== '') { + $result .= Uri::encodeComponent($temp); + } + + return $result; + } + + /** + * @return string + */ + public function value(): string + { + return $this->uri; + } + + public function __toString(): string + { + return $this->uri; + } + + /** + * @param string $uri + * @return bool + */ + public static function isTemplate(string $uri): bool + { + $open = substr_count($uri, '{'); + if ($open === 0) { + return false; + } + $close = substr_count($uri, '}'); + if ($open !== $close) { + return false; + } + + return (bool)preg_match(self::TEMPLATE_REGEX, $uri); + } + + /** + * @param string $str + * @param int $len + * @return string + */ + protected static function prefix(string $str, int $len): string + { + if ($len === 0) { + return ''; + } + + if ($len >= strlen($str)) { + // Prefix is longer than string length + return $str; + } + + return (string)UnicodeString::from($str)->substring(0, $len); + } +} \ No newline at end of file diff --git a/vendor/overtrue/pinyin/LICENSE b/vendor/overtrue/pinyin/LICENSE old mode 100644 new mode 100755 diff --git a/vendor/overtrue/pinyin/README.md b/vendor/overtrue/pinyin/README.md old mode 100644 new mode 100755 diff --git a/vendor/overtrue/pinyin/bin/pinyin b/vendor/overtrue/pinyin/bin/pinyin old mode 100644 new mode 100755 diff --git a/vendor/overtrue/pinyin/data/surnames.php b/vendor/overtrue/pinyin/data/surnames.php index 542eed226..7b31df1af 100644 --- a/vendor/overtrue/pinyin/data/surnames.php +++ b/vendor/overtrue/pinyin/data/surnames.php @@ -83,4 +83,5 @@ '若' => ' ruò ', '贠' => ' yùn ', '貟' => ' yùn ', + '么' => ' yāo ', ); diff --git a/vendor/overtrue/pinyin/data/words-5.php b/vendor/overtrue/pinyin/data/words-5.php index 7af0b9552..567cd370f 100644 --- a/vendor/overtrue/pinyin/data/words-5.php +++ b/vendor/overtrue/pinyin/data/words-5.php @@ -6751,6 +6751,7 @@ '灰' => ' huī ', '𰻝' => ' biáng ', '𰻞' => ' biáng ', + '炔' => ' guì ', '〇' => ' líng ', '㐀' => ' qiū ', '㐁' => ' tiàn ', @@ -7999,5 +8000,4 @@ '㧤' => ' chòng ', '㧥' => ' xiǎn ', '㧦' => ' xuàn ', - '㧨' => ' qiú ', ); diff --git a/vendor/overtrue/pinyin/data/words-6.php b/vendor/overtrue/pinyin/data/words-6.php index 909c23882..bc8ff7308 100644 --- a/vendor/overtrue/pinyin/data/words-6.php +++ b/vendor/overtrue/pinyin/data/words-6.php @@ -1,5 +1,6 @@ ' qiú ', '㧩' => ' pèi ', '㧪' => ' guǐ ', '㧫' => ' ér ', @@ -7999,5 +8000,4 @@ '宐' => ' yí ', '宑' => ' jǐng ', '宒' => ' zhūn ', - '宓' => ' mì ', ); diff --git a/vendor/overtrue/pinyin/data/words-7.php b/vendor/overtrue/pinyin/data/words-7.php index 79878225e..ad2b954fc 100644 --- a/vendor/overtrue/pinyin/data/words-7.php +++ b/vendor/overtrue/pinyin/data/words-7.php @@ -1,5 +1,6 @@ ' mì ', '宔' => ' zhǔ ', '宕' => ' dàng ', '宖' => ' hóng ', @@ -5375,7 +5376,6 @@ '炑' => ' mù ', '炒' => ' chǎo ', '炓' => ' liào ', - '炔' => ' guì ', '炕' => ' kàng ', '炖' => ' dùn ', '炗' => ' guāng ', diff --git a/vendor/php-mcp/schema/.github/workflows/changelog.yml b/vendor/php-mcp/schema/.github/workflows/changelog.yml new file mode 100644 index 000000000..47d29170c --- /dev/null +++ b/vendor/php-mcp/schema/.github/workflows/changelog.yml @@ -0,0 +1,31 @@ +name: "Update Changelog" + +on: + release: + types: [released] + +permissions: + contents: write + +jobs: + update: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: main + + - name: Update Changelog + uses: stefanzweifel/changelog-updater-action@v1 + with: + latest-version: ${{ github.event.release.name }} + release-notes: ${{ github.event.release.body }} + + - name: Commit updated CHANGELOG + uses: stefanzweifel/git-auto-commit-action@v5 + with: + branch: main + commit_message: Update CHANGELOG + file_pattern: CHANGELOG.md \ No newline at end of file diff --git a/vendor/php-mcp/schema/.gitignore b/vendor/php-mcp/schema/.gitignore new file mode 100644 index 000000000..456672017 --- /dev/null +++ b/vendor/php-mcp/schema/.gitignore @@ -0,0 +1,67 @@ +# Composer dependencies +/vendor/ +/composer.lock + +# PHPUnit +.phpunit.result.cache + +# PHP CS Fixer +/.php-cs-fixer.cache +/.php-cs-fixer.php + +# Editor directories and files +/.idea +/.vscode +*.sublime-project +*.sublime-workspace + +# Operating system files +.DS_Store +Thumbs.db + +# Local environment files +/.env +/.env.backup +/.env.local + +# PHP CodeSniffer +/.phpcs.xml +/.phpcs.xml.dist +/phpcs.xml +/phpcs.xml.dist + +# PHPStan +/phpstan.neon +/phpstan.neon.dist + +# Local development tools +/.php_cs +/.php_cs.cache +/.php_cs.dist +/_ide_helper.php +/.php-cs-fixer.php + +# Build artifacts +/build/ +/coverage/ + +# PHPUnit coverage reports +/clover.xml +/coverage.xml +/coverage/ + +# Laravel generated files +bootstrap/cache/ +.phpunit.result.cache + +# Local Composer dependencies +composer.phar + +workbench +playground + +# Log files +*.log + +# Cache files +cache \ No newline at end of file diff --git a/vendor/php-mcp/schema/LICENSE b/vendor/php-mcp/schema/LICENSE new file mode 100644 index 000000000..8962e8420 --- /dev/null +++ b/vendor/php-mcp/schema/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Kyrian Obikwelu + +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. \ No newline at end of file diff --git a/vendor/php-mcp/schema/README.md b/vendor/php-mcp/schema/README.md new file mode 100644 index 000000000..a38b5b63d --- /dev/null +++ b/vendor/php-mcp/schema/README.md @@ -0,0 +1,157 @@ +# PHP MCP Schema + +[![Latest Version on Packagist](https://img.shields.io/packagist/v/php-mcp/schema.svg?style=flat-square)](https://packagist.org/packages/php-mcp/schema) +[![Total Downloads](https://img.shields.io/packagist/dt/php-mcp/schema.svg?style=flat-square)](https://packagist.org/packages/php-mcp/schema) +[![License](https://img.shields.io/packagist/l/php-mcp/schema.svg?style=flat-square)](LICENSE) + +**Type-safe PHP DTOs for the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) specification.** + +This package provides comprehensive Data Transfer Objects and Enums that ensure full compliance with the official MCP schema, enabling robust server and client implementations with complete type safety. + +> 🎯 **MCP Schema Version:** [2025-03-26](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2025-03-26/schema.ts) (Latest) + +## Installation + +```bash +composer require php-mcp/schema +``` + +**Requirements:** PHP 8.1+ • No dependencies + +## Quick Start + +```php +use PhpMcp\Schema\Tool; +use PhpMcp\Schema\Resource; +use PhpMcp\Schema\Request\CallToolRequest; + +// Create a tool definition +$tool = Tool::make( + name: 'calculator', + inputSchema: [ + 'type' => 'object', + 'properties' => [ + 'operation' => ['type' => 'string'], + 'a' => ['type' => 'number'], + 'b' => ['type' => 'number'] + ], + 'required' => ['operation', 'a', 'b'] + ], + description: 'Performs basic arithmetic operations' +); + +// Serialize to JSON +$json = json_encode($tool); + +// Deserialize from array +$tool = Tool::fromArray($decodedData); +``` + +## Core Features + +### 🏗️ **Complete Schema Coverage** +Every MCP protocol type is represented with full validation and type safety. + +### 🔒 **Immutable Design** +All DTOs use readonly properties to prevent accidental mutations. + +### 🚀 **Developer Experience** +- **Factory Methods:** Convenient `make()` methods for fluent object creation +- **Array Conversion:** Seamless `toArray()` and `fromArray()` methods +- **JSON Ready:** Built-in `JsonSerializable` interface support +- **Validation:** Comprehensive input validation with clear error messages + +### 📦 **Schema Components** + +| Component | Description | +|-----------|-------------| +| **Tools** | Tool definitions with JSON Schema validation | +| **Resources** | Static and template-based resource representations | +| **Prompts** | Interactive prompt definitions with arguments | +| **Content** | Text, image, audio, and blob content types | +| **JSON-RPC** | Complete JSON-RPC 2.0 protocol implementation | +| **Requests/Results** | All 15 request types and corresponding responses | +| **Notifications** | Real-time event notification messages | +| **Capabilities** | Client and server capability declarations | + +## Usage Patterns + +### Creating Protocol Messages + +```php +// Initialize request +$request = InitializeRequest::make( + protocolVersion: '2025-03-26', + capabilities: ClientCapabilities::make(), + clientInfo: Implementation::make('MyClient', '1.0.0') +); + +// Call tool request +$callRequest = CallToolRequest::make( + name: 'calculator', + arguments: ['operation' => 'add', 'a' => 5, 'b' => 3] +); +``` + +### Working with Resources + +```php +// Static resource +$resource = Resource::make( + uri: '/data/users.json', + name: 'User Database', + description: 'Complete user registry' +); + +// Resource template +$template = ResourceTemplate::make( + uriTemplate: '/users/{id}', + name: 'User Profile', + description: 'Individual user data' +); +``` + +### Content Handling + +```php +// Text content +$text = TextContent::make('Hello, world!'); + +// Image content +$image = ImageContent::make( + data: base64_encode($imageData), + mimeType: 'image/png' +); +``` + +## Package Structure + +``` +src/ +├── Content/ # Content types (Text, Image, Audio, Blob, etc.) +├── Enum/ # Protocol enums (LoggingLevel, Role) +├── JsonRpc/ # JSON-RPC 2.0 implementation +├── Notification/ # Event notification types +├── Request/ # Protocol request messages +├── Result/ # Protocol response messages +├── Tool.php # Tool definitions +├── Resource.php # Resource representations +├── Prompt.php # Prompt definitions +└── ... # Core protocol types +``` + +## Why Use This Package? + +- ✅ **100% MCP Compliance** - Matches official specification exactly +- ✅ **Type Safety** - Catch errors at development time, not runtime +- ✅ **Zero Dependencies** - Lightweight and self-contained +- ✅ **Production Ready** - Immutable, validated, and thoroughly tested +- ✅ **Future Proof** - Updated with latest MCP specification versions + +## License + +MIT License. See [LICENSE](LICENSE) for details. + +--- + +**Part of the [PHP MCP](https://github.com/php-mcp) ecosystem** • Build type-safe MCP applications with confidence \ No newline at end of file diff --git a/vendor/php-mcp/schema/composer.json b/vendor/php-mcp/schema/composer.json new file mode 100644 index 000000000..57684575d --- /dev/null +++ b/vendor/php-mcp/schema/composer.json @@ -0,0 +1,21 @@ +{ + "name": "php-mcp/schema", + "description": "PHP Data Transfer Objects (DTOs) and Enums for the Model Context Protocol (MCP) schema.", + "type": "library", + "license": "MIT", + "autoload": { + "psr-4": { + "PhpMcp\\Schema\\": "src/" + } + }, + "authors": [ + { + "name": "Kyrian Obikwelu", + "email": "koshnawaza@gmail.com" + } + ], + "minimum-stability": "stable", + "require": { + "php": ">=8.1" + } +} diff --git a/vendor/php-mcp/schema/src/Annotations.php b/vendor/php-mcp/schema/src/Annotations.php new file mode 100644 index 000000000..0a371a373 --- /dev/null +++ b/vendor/php-mcp/schema/src/Annotations.php @@ -0,0 +1,87 @@ +priority !== null && ($this->priority < 0 || $this->priority > 1)) { + throw new \InvalidArgumentException("Annotation priority must be between 0 and 1."); + } + if ($this->audience !== null) { + foreach ($this->audience as $role) { + if (!($role instanceof Role)) { + throw new \InvalidArgumentException("All audience members must be instances of Role enum."); + } + } + } + } + + /** + * @param Role[]|null $audience Describes who the intended customer of this object or data is. + * + * It can include multiple entries to indicate content useful for multiple audiences (e.g., `[Role::User, Role::Assistant]`). + * + * @param float|null $priority Describes how important this data is for operating the server. + * + * A value of 1 means "most important," and indicates that the data is + * effectively required, while 0 means "least important," and indicates that + * the data is entirely optional. + */ + public static function make(array $audience = null, float $priority = null): static + { + return new static($audience, $priority); + } + + public function toArray(): array + { + $data = []; + if ($this->audience !== null) { + $data['audience'] = array_map(fn (Role $r) => $r->value, $this->audience); + } + if ($this->priority !== null) { + $data['priority'] = $this->priority; + } + return $data; + } + + public static function fromArray(array $data): static + { + $audience = null; + if (isset($data['audience']) && is_array($data['audience'])) { + $audience = array_map(fn (string $r) => Role::from($r), $data['audience']); + } + return new static( + $audience, + isset($data['priority']) ? (float)$data['priority'] : null + ); + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/php-mcp/schema/src/ClientCapabilities.php b/vendor/php-mcp/schema/src/ClientCapabilities.php new file mode 100644 index 000000000..766f8170c --- /dev/null +++ b/vendor/php-mcp/schema/src/ClientCapabilities.php @@ -0,0 +1,76 @@ +roots || $this->rootsListChanged) { + $data['roots'] = new \stdClass(); + if ($this->rootsListChanged !== null) { + $data['roots']->listChanged = $this->rootsListChanged; + } + } + + if ($this->sampling) { + $data['sampling'] = new \stdClass(); + } + + if ($this->experimental) { + $data['experimental'] = (object) $this->experimental; + } + + return $data; + } + + public static function fromArray(array $data): static + { + $rootsEnabled = isset($data['roots']); + $rootsListChanged = null; + if ($rootsEnabled) { + if (is_array($data['roots']) && array_key_exists('listChanged', $data['roots'])) { + $rootsListChanged = (bool) $data['roots']['listChanged']; + } elseif (is_object($data['roots']) && property_exists($data['roots'], 'listChanged')) { + $rootsListChanged = (bool) $data['roots']->listChanged; + } + } + + $sampling = null; + if (isset($data['sampling'])) { + $sampling = true; + } + + return new static( + $rootsEnabled, + $rootsListChanged, + $sampling, + $data['experimental'] ?? null + ); + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/php-mcp/schema/src/Constants.php b/vendor/php-mcp/schema/src/Constants.php new file mode 100644 index 000000000..f54b34cea --- /dev/null +++ b/vendor/php-mcp/schema/src/Constants.php @@ -0,0 +1,20 @@ + 'audio', + 'data' => $this->data, + 'mimeType' => $this->mimeType, + ]; + + if ($this->annotations !== null) { + $result['annotations'] = $this->annotations->toArray(); + } + + return $result; + } + + public static function fromArray(array $data): static + { + if (! isset($data['data']) || ! isset($data['mimeType'])) { + throw new \InvalidArgumentException("Invalid or missing 'data' or 'mimeType' in AudioContent data."); + } + + return new static($data['data'], $data['mimeType'], $data['annotations'] ?? null); + } + + /** + * Create a new AudioContent from a file path. + * + * @param string $path Path to the audio file + * @param string|null $mimeType Optional MIME type override + * @param ?Annotations $annotations Optional annotations describing the content + * + * @throws \InvalidArgumentException If the file doesn't exist + */ + public static function fromFile(string $path, ?string $mimeType = null, ?Annotations $annotations = null): static + { + if (! file_exists($path)) { + throw new \InvalidArgumentException("Audio file not found: {$path}"); + } + + $content = file_get_contents($path); + if ($content === false) { + throw new \RuntimeException("Could not read audio file: {$path}"); + } + $data = base64_encode($content); + $detectedMime = $mimeType ?? mime_content_type($path) ?: 'application/octet-stream'; + + return new static($data, $detectedMime, $annotations); + } + + /** + * Create a new AudioContent from a string. + * + * @param string $data The audio data + * @param string $mimeType MIME type of the audio + * @param ?Annotations $annotations Optional annotations describing the content + */ + public static function fromString(string $data, string $mimeType, ?Annotations $annotations = null): static + { + return new static(base64_encode($data), $mimeType, $annotations); + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/php-mcp/schema/src/Content/BlobResourceContents.php b/vendor/php-mcp/schema/src/Content/BlobResourceContents.php new file mode 100644 index 000000000..d174fcaf5 --- /dev/null +++ b/vendor/php-mcp/schema/src/Content/BlobResourceContents.php @@ -0,0 +1,62 @@ + $this->blob, + ...parent::toArray(), + ]; + } + + public static function fromArray(array $data): static + { + if (!isset($data['uri']) || !is_string($data['uri'])) { + throw new \InvalidArgumentException("Missing or invalid uri for BlobResourceContents"); + } + if (!isset($data['blob']) || !is_string($data['blob'])) { + throw new \InvalidArgumentException("Missing or invalid blob for BlobResourceContents"); + } + + return new static($data['uri'], $data['mimeType'] ?? null, $data['blob']); + } + + public static function fromStream(string $uri, $stream, string $mimeType): static + { + $blob = stream_get_contents($stream); + return new static($uri, $mimeType, base64_encode($blob)); + } + + public static function fromSplFileInfo(string $uri, \SplFileInfo $file, ?string $explicitMimeType = null): static + { + $mimeType = $explicitMimeType ?? mime_content_type($file->getPathname()); + $blob = file_get_contents($file->getPathname()); + return new static($uri, $mimeType, base64_encode($blob)); + } +} diff --git a/vendor/php-mcp/schema/src/Content/Content.php b/vendor/php-mcp/schema/src/Content/Content.php new file mode 100644 index 000000000..7e47c33dd --- /dev/null +++ b/vendor/php-mcp/schema/src/Content/Content.php @@ -0,0 +1,30 @@ + $this->type, + ]; + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/php-mcp/schema/src/Content/EmbeddedResource.php b/vendor/php-mcp/schema/src/Content/EmbeddedResource.php new file mode 100644 index 000000000..626487888 --- /dev/null +++ b/vendor/php-mcp/schema/src/Content/EmbeddedResource.php @@ -0,0 +1,119 @@ + $this->type, + 'resource' => $this->resource->toArray(), + ]; + if ($this->annotations !== null) { + $data['annotations'] = $this->annotations->toArray(); + } + return $data; + } + + public static function fromArray(array $data): static + { + if (($data['type'] ?? null) !== 'resource') { + throw new \InvalidArgumentException("Invalid type for EmbeddedResource."); + } + if (!isset($data['resource']) || !is_array($data['resource'])) { + throw new \InvalidArgumentException("Missing or invalid 'resource' field for EmbeddedResource."); + } + + $resourceData = $data['resource']; + $resourceInstance = null; + if (isset($resourceData['text'])) { + $resourceInstance = TextResourceContents::fromArray($resourceData); + } elseif (isset($resourceData['blob'])) { + $resourceInstance = BlobResourceContents::fromArray($resourceData); + } else { + throw new \InvalidArgumentException("EmbeddedResource 'resource' field must contain 'text' or 'blob'."); + } + + return new static( + $resourceInstance, + isset($data['annotations']) ? Annotations::fromArray($data['annotations']) : null + ); + } + + public static function fromText(string $uri, string $text, ?string $mimeType = 'text/plain', ?Annotations $annotations = null): static + { + $textContent = new TextResourceContents($uri, $mimeType, $text); + return new static($textContent, $annotations); + } + + public static function fromBlob(string $uri, string $base64Blob, string $mimeType, ?Annotations $annotations = null): static + { + $blobContent = new BlobResourceContents($uri, $mimeType, $base64Blob); + return new static($blobContent, $annotations); + } + + public static function fromFile(string $uri, string $path, ?string $explicitMimeType = null, ?Annotations $annotations = null): static + { + if (!file_exists($path) || !is_readable($path)) { + throw new \InvalidArgumentException("File not found or not readable: {$path}"); + } + $content = file_get_contents($path); + if ($content === false) { + throw new \RuntimeException("Could not read file: {$path}"); + } + + $guessedMimeType = $explicitMimeType ?? mime_content_type($path) ?: 'application/octet-stream'; + + if (self::isTextMimeTypeHeuristic($guessedMimeType) && mb_check_encoding($content, 'UTF-8')) { + $resourceContent = new TextResourceContents($uri, $guessedMimeType, $content); + } else { + $resourceContent = new BlobResourceContents($uri, $guessedMimeType, base64_encode($content)); + } + + return new static($resourceContent, $annotations); + } + + public static function fromStream(string $uri, $stream, string $mimeType, ?Annotations $annotations = null): static + { + $content = stream_get_contents($stream); + if ($content === false) { + throw new \RuntimeException("Could not read stream."); + } + return new static(new BlobResourceContents($uri, $mimeType, base64_encode($content)), $annotations); + } + + public static function fromSplFileInfo(string $uri, \SplFileInfo $file, ?string $explicitMimeType = null, ?Annotations $annotations = null): static + { + $content = file_get_contents($file->getPathname()); + if ($content === false) { + throw new \RuntimeException("Could not read file: {$file->getPathname()}"); + } + return new static(new BlobResourceContents($uri, $explicitMimeType ?? mime_content_type($file->getPathname()), base64_encode($content)), $annotations); + } + + private static function isTextMimeTypeHeuristic(string $mimeType): bool + { + return str_starts_with($mimeType, 'text/') || + in_array(strtolower($mimeType), ['application/json', 'application/xml', 'application/javascript', 'application/yaml']); + } +} diff --git a/vendor/php-mcp/schema/src/Content/ImageContent.php b/vendor/php-mcp/schema/src/Content/ImageContent.php new file mode 100644 index 000000000..7c624a46b --- /dev/null +++ b/vendor/php-mcp/schema/src/Content/ImageContent.php @@ -0,0 +1,82 @@ + 'image', + 'data' => $this->data, + 'mimeType' => $this->mimeType, + ]; + } + + public static function fromArray(array $data): static + { + if (!isset($data['data']) || !is_string($data['data'])) { + throw new \InvalidArgumentException("Missing or invalid 'data' in ImageContent data."); + } + if (!isset($data['mimeType']) || !is_string($data['mimeType'])) { + throw new \InvalidArgumentException("Missing or invalid 'mimeType' in ImageContent data."); + } + + return new static($data['data'], $data['mimeType']); + } + + /** + * Create a new ImageContent from a file path. + * + * @param string $path Path to the image file + * @param string|null $mimeType Optional MIME type override + * + * @throws \InvalidArgumentException If the file doesn't exist + */ + public static function fromFile(string $path, ?string $mimeType = null): static + { + if (! file_exists($path)) { + throw new \InvalidArgumentException("Image file not found: {$path}"); + } + + $data = base64_encode(file_get_contents($path)); + $detectedMime = $mimeType ?? mime_content_type($path) ?? 'image/png'; + + return new static($data, $detectedMime); + } + + public static function fromString(string $data, string $mimeType): static + { + return new static(base64_encode($data), $mimeType); + } +} diff --git a/vendor/php-mcp/schema/src/Content/PromptMessage.php b/vendor/php-mcp/schema/src/Content/PromptMessage.php new file mode 100644 index 000000000..c0a05cc55 --- /dev/null +++ b/vendor/php-mcp/schema/src/Content/PromptMessage.php @@ -0,0 +1,70 @@ + TextContent::fromArray($contentData), + 'image' => ImageContent::fromArray($contentData), + 'audio' => AudioContent::fromArray($contentData), + 'resource' => EmbeddedResource::fromArray($contentData), + default => throw new \InvalidArgumentException("Invalid content type '{$contentType}' for PromptMessage.") + }; + + return new static( + role: Role::from($data['role']), + content: $content + ); + } + + /** + * Convert the message to an array. + * + * @return array{role: Role, content: array} + */ + public function toArray(): array + { + return [ + 'role' => $this->role->value, + 'content' => $this->content->toArray(), + ]; + } +} diff --git a/vendor/php-mcp/schema/src/Content/ResourceContents.php b/vendor/php-mcp/schema/src/Content/ResourceContents.php new file mode 100644 index 000000000..0177877bc --- /dev/null +++ b/vendor/php-mcp/schema/src/Content/ResourceContents.php @@ -0,0 +1,45 @@ + $this->uri]; + if ($this->mimeType !== null) { + $data['mimeType'] = $this->mimeType; + } + return $data; + } + + public static function fromArray(array $data): static + { + if (!isset($data['uri']) || !is_string($data['uri'])) { + throw new \InvalidArgumentException("Missing or invalid 'uri' in ResourceContents data."); + } + return new static($data['uri'], $data['mimeType'] ?? null); + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/php-mcp/schema/src/Content/SamplingMessage.php b/vendor/php-mcp/schema/src/Content/SamplingMessage.php new file mode 100644 index 000000000..35a0fc4f0 --- /dev/null +++ b/vendor/php-mcp/schema/src/Content/SamplingMessage.php @@ -0,0 +1,65 @@ + $this->role->value, + 'content' => $this->content->toArray(), + ]; + } + + public static function fromArray(array $data): static + { + if (!isset($data['role']) || !is_string($data['role'])) { + throw new \InvalidArgumentException("Missing or invalid 'role' in SamplingMessage data."); + } + if (!isset($data['content']) || !is_array($data['content'])) { + throw new \InvalidArgumentException("Missing or invalid 'content' in SamplingMessage data."); + } + + $role = Role::from($data['role']); + $contentData = $data['content']; + $contentType = $contentData['type'] ?? null; + + $contentInstance = match ($contentType) { + 'text' => TextContent::fromArray($contentData), + 'image' => ImageContent::fromArray($contentData), + 'audio' => AudioContent::fromArray($contentData), + default => throw new \InvalidArgumentException("Invalid content type '{$contentType}' for SamplingMessage.") + }; + + return new static($role, $contentInstance); + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/php-mcp/schema/src/Content/TextContent.php b/vendor/php-mcp/schema/src/Content/TextContent.php new file mode 100644 index 000000000..fd9de8a6d --- /dev/null +++ b/vendor/php-mcp/schema/src/Content/TextContent.php @@ -0,0 +1,81 @@ + 'text', + 'text' => $this->text, + ]; + + if ($this->annotations !== null) { + $result['annotations'] = $this->annotations->toArray(); + } + + return $result; + } + + public static function fromArray(array $data): static + { + if (!isset($data['text']) || !is_string($data['text'])) { + throw new \InvalidArgumentException("Missing or invalid 'text' in TextContent data."); + } + + return new static($data['text'], $data['annotations'] ?? null); + } + + /** + * Create a new TextContent from any simple value. + * + * @param mixed $value The value to convert to text + */ + public static function make(mixed $value, ?Annotations $annotations = null): static + { + if (is_array($value) || is_object($value)) { + $text = json_encode($value, JSON_PRETTY_PRINT); + + return new static($text, $annotations); + } + + return new static((string) $value, $annotations); + } + + /** + * Create a new TextContent with markdown formatted code. + * + * @param string $code The code to format + * @param string $language The language for syntax highlighting + */ + public static function code(string $code, string $language = '', ?Annotations $annotations = null): static + { + return new static("```{$language}\n{$code}\n```", $annotations); + } +} diff --git a/vendor/php-mcp/schema/src/Content/TextResourceContents.php b/vendor/php-mcp/schema/src/Content/TextResourceContents.php new file mode 100644 index 000000000..5a3777d7c --- /dev/null +++ b/vendor/php-mcp/schema/src/Content/TextResourceContents.php @@ -0,0 +1,49 @@ + $this->text, + ...parent::toArray(), + ]; + } + + public static function make(string $uri, ?string $mimeType, string $text): static + { + return new static($uri, $mimeType, $text); + } + + public static function fromArray(array $data): static + { + if (!isset($data['uri']) || !is_string($data['uri'])) { + throw new \InvalidArgumentException("Missing or invalid uri for TextResourceContents"); + } + if (!isset($data['text']) || !is_string($data['text'])) { + throw new \InvalidArgumentException("Missing or invalid text for TextResourceContents"); + } + + return new static($data['uri'], $data['mimeType'] ?? null, $data['text']); + } +} diff --git a/vendor/php-mcp/schema/src/Enum/LoggingLevel.php b/vendor/php-mcp/schema/src/Enum/LoggingLevel.php new file mode 100644 index 000000000..cb6113b1d --- /dev/null +++ b/vendor/php-mcp/schema/src/Enum/LoggingLevel.php @@ -0,0 +1,23 @@ + $this->name, + 'version' => $this->version, + ]; + } + + public static function fromArray(array $data): static + { + if (empty($data['name']) || !is_string($data['name'])) { + throw new \InvalidArgumentException("Invalid or missing 'name' in Implementation data."); + } + if (empty($data['version']) || !is_string($data['version'])) { + throw new \InvalidArgumentException("Invalid or missing 'version' in Implementation data."); + } + return new static($data['name'], $data['version']); + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/php-mcp/schema/src/JsonRpc/BatchRequest.php b/vendor/php-mcp/schema/src/JsonRpc/BatchRequest.php new file mode 100644 index 000000000..096c5c92d --- /dev/null +++ b/vendor/php-mcp/schema/src/JsonRpc/BatchRequest.php @@ -0,0 +1,157 @@ + $requests The individual requests/notifications in this batch. + */ + public function __construct(public readonly array $items) + { + foreach ($items as $item) { + if (!($item instanceof Request || $item instanceof Notification)) { + throw new \InvalidArgumentException("All items in BatchRequest must be instances of Request or Notification."); + } + } + } + + public function getId(): string|int|null + { + foreach ($this->items as $item) { + if ($item instanceof Request) { + return $item->getId(); + } + } + return ""; + } + + /** + * Create a new JSON-RPC 2.0 batch of requests/notifications. + * @param array $requests The individual requests/notifications in this batch. + */ + public static function make(array $items): static + { + return new static($items); + } + + public function toArray(): array + { + return array_map(fn($item) => $item->toArray(), $this->items); + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } + + public static function fromArray(array $data): static + { + if (empty($data)) { + throw new \InvalidArgumentException("BatchRequest data array must not be empty."); + } + + $items = []; + foreach ($data as $itemData) { + if (!is_array($itemData)) { + throw new \InvalidArgumentException("BatchRequest item data must be an array."); + } + if (isset($itemData['id']) && $itemData['id'] !== null) { + $items[] = Request::fromArray($itemData); + } elseif (isset($itemData['method'])) { + $items[] = Notification::fromArray($itemData); + } else { + throw new \InvalidArgumentException("Invalid item in BatchRequest data: missing 'method' or 'id'."); + } + } + + return new static($items); + } + + /** + * Check if this batch has any requests. + */ + public function hasRequests(): bool + { + $hasRequests = false; + foreach ($this->items as $item) { + if ($item instanceof Request) { + $hasRequests = true; + break; + } + } + + return $hasRequests; + } + + /** + * Check if this batch has any notifications. + */ + public function hasNotifications(): bool + { + $hasNotifications = false; + foreach ($this->items as $item) { + if ($item instanceof Notification) { + $hasNotifications = true; + break; + } + } + return $hasNotifications; + } + + /** + * Get all requests in this batch. + * + * @return array + */ + public function getRequests(): array + { + return array_filter($this->items, fn($item) => $item instanceof Request); + } + + /** + * Get all notifications in this batch. + * + * @return array + */ + public function getNotifications(): array + { + return array_filter($this->items, fn($item) => $item instanceof Notification); + } + + /** + * Get all elements in this batch. + * + * @return array + */ + public function getAll(): array + { + return $this->items; + } + + /** + * Count the total number of elements in this batch. + */ + public function count(): int + { + return count($this->items); + } + + public function nRequests(): int + { + return count($this->getRequests()); + } + + public function nNotifications(): int + { + return count($this->getNotifications()); + } +} diff --git a/vendor/php-mcp/schema/src/JsonRpc/BatchResponse.php b/vendor/php-mcp/schema/src/JsonRpc/BatchResponse.php new file mode 100644 index 000000000..d4e152805 --- /dev/null +++ b/vendor/php-mcp/schema/src/JsonRpc/BatchResponse.php @@ -0,0 +1,122 @@ + $items The individual responses/errors in this batch. + */ + public function __construct(public array $items) + { + foreach ($items as $item) { + if (!($item instanceof Response || $item instanceof Error)) { + throw new \InvalidArgumentException("All items in BatchResponse must be instances of Response or Error."); + } + } + } + + public function getId(): string|int|null + { + foreach ($this->items as $item) { + if ($item instanceof Response) { + return $item->getId(); + } + } + return ""; + } + + /** + * @param array $items The individual responses/errors in this batch. + */ + public static function make(array $items): static + { + return new static($items); + } + + public function toArray(): array + { + return array_map(fn($item) => $item->toArray(), $this->items); + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } + + public static function fromArray(array $data): static + { + if (empty($data)) { + throw new \InvalidArgumentException("BatchResponse data array must not be empty."); + } + + $items = []; + foreach ($data as $itemData) { + if (!is_array($itemData)) { + throw new \InvalidArgumentException("BatchResponse item data must be an array."); + } + if (isset($itemData['id']) && $itemData['id'] !== null) { + $items[] = Response::fromArray($itemData); + } elseif (isset($itemData['error'])) { + $items[] = Error::fromArray($itemData); + } else { + throw new \InvalidArgumentException("Invalid item in BatchResponse data: missing 'id' or 'error'."); + } + } + + return new static($items); + } + + public function hasResponses(): bool + { + $hasResponses = false; + foreach ($this->items as $item) { + if ($item instanceof Response) { + $hasResponses = true; + break; + } + } + + return $hasResponses; + } + + public function hasErrors(): bool + { + $hasErrors = false; + foreach ($this->items as $item) { + if ($item instanceof Error) { + $hasErrors = true; + break; + } + } + return $hasErrors; + } + + public function getResponses(): array + { + return array_filter($this->items, fn($item) => $item instanceof Response); + } + + public function getErrors(): array + { + return array_filter($this->items, fn($item) => $item instanceof Error); + } + + public function getAll(): array + { + return $this->items; + } + + public function count(): int + { + return count($this->items); + } +} diff --git a/vendor/php-mcp/schema/src/JsonRpc/Error.php b/vendor/php-mcp/schema/src/JsonRpc/Error.php new file mode 100644 index 000000000..4b447f4cf --- /dev/null +++ b/vendor/php-mcp/schema/src/JsonRpc/Error.php @@ -0,0 +1,115 @@ +id; + } + + /** + * @param string|int $id The request id that this error is associated with. + * @param int $code The error type that occurred. + * @param string $message A short description of the error. + * @param mixed|null $data Additional information about the error. + */ + public static function make(string|int $id, int $code, string $message, mixed $data = null): static + { + return new static(Constants::JSONRPC_VERSION, $id, $code, $message, $data); + } + + public function toArray(): array + { + $error = [ + 'code' => $this->code, + 'message' => $this->message, + ]; + + if ($this->data !== null) { + $error['data'] = $this->data; + } + + return [ + 'jsonrpc' => $this->jsonrpc, + 'id' => $this->id, + 'error' => $error, + ]; + } + + public static function fromArray(array $data): static + { + if (!isset($data['jsonrpc']) || $data['jsonrpc'] !== Constants::JSONRPC_VERSION) { + throw new \InvalidArgumentException('Invalid or missing "jsonrpc" in Error data.'); + } + if (!isset($data['id']) || !is_string($data['id'])) { + throw new \InvalidArgumentException('Invalid or missing "id" in Error data.'); + } + if (!isset($data['code']) || !is_int($data['code'])) { + throw new \InvalidArgumentException('Invalid or missing "code" in Error data.'); + } + if (!isset($data['message']) || !is_string($data['message'])) { + throw new \InvalidArgumentException('Invalid or missing "message" in Error data.'); + } + return new static( + $data['jsonrpc'], + $data['id'], + $data['code'], + $data['message'], + $data['data'] ?? null + ); + } + + public static function forParseError(string $message, string|int $id = ''): static + { + return new static(Constants::JSONRPC_VERSION, $id, Constants::PARSE_ERROR, $message); + } + + public static function forInvalidRequest(string $message, string|int $id = ''): static + { + return new static(Constants::JSONRPC_VERSION, $id, Constants::INVALID_REQUEST, $message); + } + + public static function forMethodNotFound(string $message, string|int $id = ''): static + { + return new static(Constants::JSONRPC_VERSION, $id, Constants::METHOD_NOT_FOUND, $message); + } + + public static function forInvalidParams(string $message, string|int $id = ''): static + { + return new static(Constants::JSONRPC_VERSION, $id, Constants::INVALID_PARAMS, $message); + } + + public static function forInternalError(string $message, string|int $id = ''): static + { + return new static(Constants::JSONRPC_VERSION, $id, Constants::INTERNAL_ERROR, $message); + } + + public static function forServerError(string $message, string|int $id = ''): static + { + return new static(Constants::JSONRPC_VERSION, $id, Constants::SERVER_ERROR, $message); + } +} diff --git a/vendor/php-mcp/schema/src/JsonRpc/Message.php b/vendor/php-mcp/schema/src/JsonRpc/Message.php new file mode 100644 index 000000000..d363801ff --- /dev/null +++ b/vendor/php-mcp/schema/src/JsonRpc/Message.php @@ -0,0 +1,27 @@ +toArray(); + } + + abstract public function toArray(): array; +} diff --git a/vendor/php-mcp/schema/src/JsonRpc/Notification.php b/vendor/php-mcp/schema/src/JsonRpc/Notification.php new file mode 100644 index 000000000..bf53194f0 --- /dev/null +++ b/vendor/php-mcp/schema/src/JsonRpc/Notification.php @@ -0,0 +1,61 @@ +|null $params Parameters for the method. + */ + public function __construct( + string $jsonrpc, + public readonly string $method, + public readonly ?array $params = null + ) { + parent::__construct($jsonrpc); + } + + public function getId(): string|int|null + { + return null; + } + + public function toArray(): array + { + $array = [ + 'jsonrpc' => $this->jsonrpc, + 'method' => $this->method, + ]; + if ($this->params !== null) { + $array['params'] = $this->params; + } + return $array; + } + + public static function fromArray(array $data): static + { + if (($data['jsonrpc'] ?? null) !== Constants::JSONRPC_VERSION) { + throw new \InvalidArgumentException('Invalid or missing "jsonrpc" version for Notification.'); + } + if (isset($data['id'])) { + throw new \InvalidArgumentException('Notification MUST NOT contain an "id" field.'); + } + if (!isset($data['method']) || !is_string($data['method'])) { + throw new \InvalidArgumentException('Invalid or missing "method" for Notification.'); + } + $params = $data['params'] ?? null; + if ($params !== null && !is_array($params)) { + throw new \InvalidArgumentException('"params" for Notification must be an array/object or null.'); + } + return new static( + $data['jsonrpc'], + $data['method'], + $params + ); + } +} diff --git a/vendor/php-mcp/schema/src/JsonRpc/Parser.php b/vendor/php-mcp/schema/src/JsonRpc/Parser.php new file mode 100644 index 000000000..b5fa19e79 --- /dev/null +++ b/vendor/php-mcp/schema/src/JsonRpc/Parser.php @@ -0,0 +1,134 @@ +|null $params Parameters for the method. + */ + public function __construct( + string $jsonrpc, + public readonly string|int $id, + public readonly string $method, + public readonly ?array $params = null + ) { + parent::__construct($jsonrpc); + if ($this->id === null) { + throw new \InvalidArgumentException("JSON-RPC Request ID MUST NOT be null for MCP."); + } + } + + public function getId(): string|int|null + { + return $this->id; + } + + public static function fromArray(array $data): static + { + if (($data['jsonrpc'] ?? null) !== Constants::JSONRPC_VERSION) { + throw new \InvalidArgumentException('Invalid or missing "jsonrpc" version for Request.'); + } + if (!isset($data['id']) || !is_string($data['id']) && !is_int($data['id'])) { + throw new \InvalidArgumentException('Invalid or missing "id" for Request.'); + } + if (!isset($data['method']) || !is_string($data['method'])) { + throw new \InvalidArgumentException('Invalid or missing "method" for Request.'); + } + $params = $data['params'] ?? null; + if ($params instanceof \stdClass) { + $params = (array) $params; + } + if ($params !== null && !is_array($params)) { + throw new \InvalidArgumentException('"params" for Request must be an array/object or null.'); + } + return new static( + $data['jsonrpc'], + $data['id'], + $data['method'], + $params + ); + } + + public function toArray(): array + { + $array = [ + 'jsonrpc' => $this->jsonrpc, + 'id' => $this->id, + 'method' => $this->method, + ]; + if ($this->params !== null) { + $array['params'] = $this->params; + } + return $array; + } +} diff --git a/vendor/php-mcp/schema/src/JsonRpc/Response.php b/vendor/php-mcp/schema/src/JsonRpc/Response.php new file mode 100644 index 000000000..5056ec655 --- /dev/null +++ b/vendor/php-mcp/schema/src/JsonRpc/Response.php @@ -0,0 +1,75 @@ +id; + } + + /** + * @param string|int $id This MUST be the same as the value of the id member in the Request Object. + * @param Result|array $result The value of this member is determined by the method invoked on the Server. + */ + public static function make(string|int $id, Result|array $result): static + { + return new static(Constants::JSONRPC_VERSION, $id, $result); + } + + public function toArray(): array + { + return [ + 'jsonrpc' => $this->jsonrpc, + 'id' => $this->id, + 'result' => is_array($this->result) ? $this->result : $this->result->toArray(), + ]; + } + + public function jsonSerialize(): array + { + return [ + 'jsonrpc' => $this->jsonrpc, + 'id' => $this->id, + 'result' => is_array($this->result) ? $this->result : $this->result->jsonSerialize(), + ]; + } + + public static function fromArray(array $data): static + { + if (($data['jsonrpc'] ?? null) !== Constants::JSONRPC_VERSION) { + throw new \InvalidArgumentException('Invalid or missing "jsonrpc" version for Response.'); + } + if (!isset($data['id'])) { + throw new \InvalidArgumentException('Missing "id" for Response.'); + } + if (!is_string($data['id']) && !is_int($data['id'])) { + throw new \InvalidArgumentException('Invalid "id" type for Response.'); + } + if (!isset($data['result'])) { + throw new \InvalidArgumentException('Response must contain "result" field.'); + } + return new static( + $data['jsonrpc'], + $data['id'], + $data['result'] + ); + } +} diff --git a/vendor/php-mcp/schema/src/JsonRpc/Result.php b/vendor/php-mcp/schema/src/JsonRpc/Result.php new file mode 100644 index 000000000..6918ea90a --- /dev/null +++ b/vendor/php-mcp/schema/src/JsonRpc/Result.php @@ -0,0 +1,25 @@ +toArray(); + } +} diff --git a/vendor/php-mcp/schema/src/ModelHint.php b/vendor/php-mcp/schema/src/ModelHint.php new file mode 100644 index 000000000..59b2d0440 --- /dev/null +++ b/vendor/php-mcp/schema/src/ModelHint.php @@ -0,0 +1,53 @@ +name === null) { + return []; + } + + return ['name' => $this->name]; + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/php-mcp/schema/src/ModelPreferences.php b/vendor/php-mcp/schema/src/ModelPreferences.php new file mode 100644 index 000000000..2151d9f49 --- /dev/null +++ b/vendor/php-mcp/schema/src/ModelPreferences.php @@ -0,0 +1,81 @@ +hints !== null) { + $result['hints'] = array_map(fn ($hint) => $hint->toArray(), $this->hints); + } + if ($this->costPriority !== null) { + $result['costPriority'] = $this->costPriority; + } + if ($this->speedPriority !== null) { + $result['speedPriority'] = $this->speedPriority; + } + if ($this->intelligencePriority !== null) { + $result['intelligencePriority'] = $this->intelligencePriority; + } + return $result; + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/php-mcp/schema/src/Notification/CancelledNotification.php b/vendor/php-mcp/schema/src/Notification/CancelledNotification.php new file mode 100644 index 000000000..2f9490e2c --- /dev/null +++ b/vendor/php-mcp/schema/src/Notification/CancelledNotification.php @@ -0,0 +1,69 @@ + $this->requestId, + ]; + if ($this->reason !== null) { + $params['reason'] = $this->reason; + } + if ($_meta !== null) { + $params['_meta'] = $_meta; + } + + parent::__construct(Constants::JSONRPC_VERSION, 'notifications/cancelled', $params); + } + + /** + * @param string|int $id The ID of the request to cancel. + * @param string $requestId The ID of the request that is being cancelled. This MUST correspond to the ID of a request previously issued in the same direction. + * @param string|null $reason An optional string describing the reason for the cancellation. This MAY be logged or presented to the user. + * @param array|null $_meta Additional metadata about the notification. + */ + public static function make(string $requestId, ?string $reason = null, ?array $_meta = null): static + { + return new static($requestId, $reason, $_meta); + } + + public static function fromNotification(Notification $notification): static + { + if ($notification->method !== 'notifications/cancelled') { + throw new \InvalidArgumentException('Notification is not a notifications/cancelled notification'); + } + + $params = $notification->params; + + if (! isset($params['requestId']) || ! is_string($params['requestId'])) { + throw new \InvalidArgumentException('Missing or invalid requestId parameter for notifications/cancelled notification'); + } + + return new static($params['requestId'], $params['reason'] ?? null, $params['_meta'] ?? null); + } +} diff --git a/vendor/php-mcp/schema/src/Notification/InitializedNotification.php b/vendor/php-mcp/schema/src/Notification/InitializedNotification.php new file mode 100644 index 000000000..ba8099197 --- /dev/null +++ b/vendor/php-mcp/schema/src/Notification/InitializedNotification.php @@ -0,0 +1,40 @@ +method !== 'notifications/initialized') { + throw new \InvalidArgumentException('Notification is not a notifications/initialized notification'); + } + + return new static($notification->params['_meta'] ?? null); + } +} diff --git a/vendor/php-mcp/schema/src/Notification/LoggingMessageNotification.php b/vendor/php-mcp/schema/src/Notification/LoggingMessageNotification.php new file mode 100644 index 000000000..c5edb812a --- /dev/null +++ b/vendor/php-mcp/schema/src/Notification/LoggingMessageNotification.php @@ -0,0 +1,72 @@ + $level->value, + 'data' => is_string($data) ? $data : json_encode($data), + ]; + + if ($logger !== null) { + $params['logger'] = $logger; + } + + if ($_meta !== null) { + $params['_meta'] = $_meta; + } + + parent::__construct(Constants::JSONRPC_VERSION, 'notifications/message', $params); + } + + /** + * @param LoggingLevel $level The severity of this log message. + * @param mixed $data The data to be logged, such as a string message or an object. Any JSON serializable type is allowed here. + * @param string|null $logger An optional name of the logger issuing this message. + * @param array|null $_meta Optional metadata to include in the notification. + */ + public static function make(LoggingLevel $level, mixed $data, ?string $logger = null, ?array $_meta = null): static + { + return new static($level, $data, $logger, $_meta); + } + + public static function fromNotification(Notification $notification): static + { + if ($notification->method !== 'notifications/message') { + throw new \InvalidArgumentException('Notification is not a notifications/message notification'); + } + + $params = $notification->params; + + if (! isset($params['level']) || ! is_string($params['level'])) { + throw new \InvalidArgumentException('Missing or invalid level parameter for notifications/message notification'); + } + + if (! isset($params['data'])) { + throw new \InvalidArgumentException('Missing data parameter for notifications/message notification'); + } + + $level = LoggingLevel::from($params['level']); + + return new static($level, $params['data'], $params['logger'] ?? null, $params['_meta'] ?? null); + } +} diff --git a/vendor/php-mcp/schema/src/Notification/ProgressNotification.php b/vendor/php-mcp/schema/src/Notification/ProgressNotification.php new file mode 100644 index 000000000..45d18721b --- /dev/null +++ b/vendor/php-mcp/schema/src/Notification/ProgressNotification.php @@ -0,0 +1,67 @@ +method !== 'notifications/progress') { + throw new \InvalidArgumentException('Notification is not a notifications/progress notification'); + } + + $params = $notification->params; + + if (! isset($params['progressToken']) || ! is_string($params['progressToken'])) { + throw new \InvalidArgumentException('Missing or invalid progressToken parameter for notifications/progress notification'); + } + + if (! isset($params['progress']) || ! is_float($params['progress'])) { + throw new \InvalidArgumentException('Missing or invalid progress parameter for notifications/progress notification'); + } + + return new static($params['progressToken'], $params['progress'], $params['total'] ?? null, $params['message'] ?? null, $params['_meta'] ?? null); + } +} diff --git a/vendor/php-mcp/schema/src/Notification/PromptListChangedNotification.php b/vendor/php-mcp/schema/src/Notification/PromptListChangedNotification.php new file mode 100644 index 000000000..8e888db75 --- /dev/null +++ b/vendor/php-mcp/schema/src/Notification/PromptListChangedNotification.php @@ -0,0 +1,39 @@ +method !== 'notifications/prompts/list_changed') { + throw new \InvalidArgumentException('Notification is not a notifications/prompts/list_changed notification'); + } + + return new static($notification->params['_meta'] ?? null); + } +} diff --git a/vendor/php-mcp/schema/src/Notification/ResourceListChangedNotification.php b/vendor/php-mcp/schema/src/Notification/ResourceListChangedNotification.php new file mode 100644 index 000000000..ecbf2470f --- /dev/null +++ b/vendor/php-mcp/schema/src/Notification/ResourceListChangedNotification.php @@ -0,0 +1,39 @@ +method !== 'notifications/resources/list_changed') { + throw new \InvalidArgumentException('Notification is not a notifications/resources/list_changed notification'); + } + + return new static($notification->params['_meta'] ?? null); + } +} diff --git a/vendor/php-mcp/schema/src/Notification/ResourceUpdatedNotification.php b/vendor/php-mcp/schema/src/Notification/ResourceUpdatedNotification.php new file mode 100644 index 000000000..c152033a6 --- /dev/null +++ b/vendor/php-mcp/schema/src/Notification/ResourceUpdatedNotification.php @@ -0,0 +1,43 @@ + $uri]; + if ($_meta !== null) { + $params['_meta'] = $_meta; + } + + parent::__construct(Constants::JSONRPC_VERSION, 'notifications/resources/updated', $params); + } + + public static function make(string $uri, ?array $_meta = null): static + { + return new static($uri, $_meta); + } + + public static function fromNotification(Notification $notification): static + { + if ($notification->method !== 'notifications/resources/updated') { + throw new \InvalidArgumentException('Notification is not a notifications/resources/updated notification'); + } + + $params = $notification->params; + + if (! isset($params['uri']) || ! is_string($params['uri'])) { + throw new \InvalidArgumentException('Missing or invalid uri parameter for notifications/resources/updated notification'); + } + + return new static($params['uri'], $params['_meta'] ?? null); + } +} diff --git a/vendor/php-mcp/schema/src/Notification/RootsListChangedNotification.php b/vendor/php-mcp/schema/src/Notification/RootsListChangedNotification.php new file mode 100644 index 000000000..53c003703 --- /dev/null +++ b/vendor/php-mcp/schema/src/Notification/RootsListChangedNotification.php @@ -0,0 +1,41 @@ +method !== 'notifications/roots/list_changed') { + throw new \InvalidArgumentException('Notification is not a notifications/roots/list_changed notification'); + } + + return new static($notification->params['_meta'] ?? null); + } +} diff --git a/vendor/php-mcp/schema/src/Notification/ToolListChangedNotification.php b/vendor/php-mcp/schema/src/Notification/ToolListChangedNotification.php new file mode 100644 index 000000000..abebdd74a --- /dev/null +++ b/vendor/php-mcp/schema/src/Notification/ToolListChangedNotification.php @@ -0,0 +1,39 @@ +method !== 'notifications/tools/list_changed') { + throw new \InvalidArgumentException('Notification is not a notifications/tools/list_changed notification'); + } + + return new static($notification->params['_meta'] ?? null); + } +} diff --git a/vendor/php-mcp/schema/src/Prompt.php b/vendor/php-mcp/schema/src/Prompt.php new file mode 100644 index 000000000..dfc7e10e9 --- /dev/null +++ b/vendor/php-mcp/schema/src/Prompt.php @@ -0,0 +1,75 @@ +arguments !== null) { + foreach ($this->arguments as $arg) { + if (!($arg instanceof PromptArgument)) { + throw new \InvalidArgumentException("All items in Prompt 'arguments' must be PromptArgument instances."); + } + } + } + } + + /** + * @param string $name The name of the prompt or prompt template. + * @param string|null $description An optional description of what this prompt provides. + * @param PromptArgument[]|null $arguments A list of arguments for templating. Null if not a template. + */ + public static function make(string $name, ?string $description = null, ?array $arguments = null): static + { + return new static($name, $description, $arguments); + } + + public function toArray(): array + { + $data = ['name' => $this->name]; + if ($this->description !== null) { + $data['description'] = $this->description; + } + if ($this->arguments !== null) { + $data['arguments'] = array_map(fn (PromptArgument $arg) => $arg->toArray(), $this->arguments); + } + return $data; + } + + public static function fromArray(array $data): static + { + if (empty($data['name']) || !is_string($data['name'])) { + throw new \InvalidArgumentException("Invalid or missing 'name' in Prompt data."); + } + $arguments = null; + if (isset($data['arguments']) && is_array($data['arguments'])) { + $arguments = array_map(fn (array $argData) => PromptArgument::fromArray($argData), $data['arguments']); + } + return new static( + name: $data['name'], + description: $data['description'] ?? null, + arguments: $arguments + ); + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/php-mcp/schema/src/PromptArgument.php b/vendor/php-mcp/schema/src/PromptArgument.php new file mode 100644 index 000000000..883a77c87 --- /dev/null +++ b/vendor/php-mcp/schema/src/PromptArgument.php @@ -0,0 +1,64 @@ + $this->name]; + if ($this->description !== null) { + $data['description'] = $this->description; + } + if ($this->required !== null) { + $data['required'] = $this->required; + } // Only include if explicitly set + return $data; + } + + public static function fromArray(array $data): static + { + if (empty($data['name']) || !is_string($data['name'])) { + throw new \InvalidArgumentException("Invalid or missing 'name' in PromptArgument data."); + } + return new static( + name: $data['name'], + description: $data['description'] ?? null, + required: $data['required'] ?? null // Keep null if not present, MCP implies default false + ); + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/php-mcp/schema/src/PromptReference.php b/vendor/php-mcp/schema/src/PromptReference.php new file mode 100644 index 000000000..f65d4ff5a --- /dev/null +++ b/vendor/php-mcp/schema/src/PromptReference.php @@ -0,0 +1,40 @@ + $this->type, + 'name' => $this->name, + ]; + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/php-mcp/schema/src/Request/CallToolRequest.php b/vendor/php-mcp/schema/src/Request/CallToolRequest.php new file mode 100644 index 000000000..c57c71349 --- /dev/null +++ b/vendor/php-mcp/schema/src/Request/CallToolRequest.php @@ -0,0 +1,78 @@ + $arguments The arguments to pass to the tool. + * @param array|null $_meta Optional metadata to include in the request. + */ + public function __construct( + string|int $id, + public readonly string $name, + public readonly array $arguments, + public readonly ?array $_meta = null + ) { + $params = [ + 'name' => $name, + 'arguments' => (object) $arguments, + ]; + + if ($_meta !== null) { + $params['_meta'] = $_meta; + } + + parent::__construct(Constants::JSONRPC_VERSION, $id, 'tools/call', $params); + } + + /** + * @param string|int $id The ID of the request to cancel. + * @param string $name The name of the tool to invoke. + * @param array $arguments The arguments to pass to the tool. + * @param array|null $_meta Optional metadata to include in the request. + */ + public static function make(string|int $id, string $name, array $arguments, ?array $_meta = null): static + { + return new static($id, $name, $arguments, $_meta); + } + + public static function fromRequest(Request $request): static + { + if ($request->method !== 'tools/call') { + throw new \InvalidArgumentException('Request is not a call tool request'); + } + + $params = $request->params ?? []; + + if (!isset($params['name']) || !is_string($params['name'])) { + throw new \InvalidArgumentException("Missing or invalid 'name' parameter for tools/call."); + } + + $arguments = $params['arguments'] ?? []; + + if ($arguments instanceof \stdClass) { + $arguments = (array) $arguments; + } + + if (!is_array($arguments)) { + throw new \InvalidArgumentException("Parameter 'arguments' must be an array."); + } + + return new static( + $request->id, + $params['name'], + $arguments, + $params['_meta'] ?? null + ); + } +} diff --git a/vendor/php-mcp/schema/src/Request/CompletionCompleteRequest.php b/vendor/php-mcp/schema/src/Request/CompletionCompleteRequest.php new file mode 100644 index 000000000..d98c33f59 --- /dev/null +++ b/vendor/php-mcp/schema/src/Request/CompletionCompleteRequest.php @@ -0,0 +1,74 @@ + $this->ref->toArray(), + 'argument' => $this->argument, + ]; + + if ($this->_meta !== null) { + $params['_meta'] = $this->_meta; + } + + parent::__construct(Constants::JSONRPC_VERSION, $id, 'completion/complete', $params); + } + + /** + * @param string|int $id The ID of the request to cancel. + * @param PromptReference|ResourceReference $ref The prompt or resource to complete. + * @param array $argument The arguments to complete. + * @param array|null $_meta Optional metadata to include in the request. + */ + public static function make(string|int $id, PromptReference|ResourceReference $ref, array $argument, ?array $_meta = null): static + { + return new static($id, $ref, $argument, $_meta); + } + + public static function fromRequest(Request $request): static + { + if ($request->method !== 'completion/complete') { + throw new \InvalidArgumentException('Request is not a completion/complete request'); + } + + $params = $request->params; + + if (! isset($params['ref']) || ! is_array($params['ref'])) { + throw new \InvalidArgumentException("Missing or invalid 'ref' parameter for completion/complete."); + } + + $ref = match ($params['ref']['type'] ?? null) { + 'ref/prompt' => new PromptReference($params['ref']['name']), + 'ref/resource' => new ResourceReference($params['ref']['uri']), + default => throw new \InvalidArgumentException("Invalid 'ref' parameter for completion/complete."), + }; + + if (! isset($params['argument']) || ! is_array($params['argument'])) { + throw new \InvalidArgumentException("Missing or invalid 'argument' parameter for completion/complete."); + } + + return new static($request->id, $ref, $params['argument'], $params['_meta'] ?? null); + } +} diff --git a/vendor/php-mcp/schema/src/Request/CreateSamplingMesssageRequest.php b/vendor/php-mcp/schema/src/Request/CreateSamplingMesssageRequest.php new file mode 100644 index 000000000..66ee026f8 --- /dev/null +++ b/vendor/php-mcp/schema/src/Request/CreateSamplingMesssageRequest.php @@ -0,0 +1,89 @@ + array_map(fn (SamplingMessage $message) => $message->toArray(), $this->messages), + 'maxTokens' => $this->maxTokens, + ]; + + if ($this->preferences !== null) { + $params['preferences'] = $this->preferences->toArray(); + } + + if ($this->systemPrompt !== null) { + $params['systemPrompt'] = $this->systemPrompt; + } + + if ($this->includeContext !== null) { + $params['includeContext'] = $this->includeContext; + } + + if ($this->temperature !== null) { + $params['temperature'] = $this->temperature; + } + + if ($this->stopSequences !== null) { + $params['stopSequences'] = $this->stopSequences; + } + + if ($this->metadata !== null) { + $params['metadata'] = $this->metadata; + } + + if ($this->_meta !== null) { + $params['_meta'] = $this->_meta; + } + + parent::__construct(Constants::JSONRPC_VERSION, $id, 'sampling/createMessage', $params); + } + + /** + * @param string|int $id The ID of the request to cancel. + * @param SamplingMessage[] $messages The messages to send to the model. + * @param int $maxTokens The maximum number of tokens to sample, as requested by the server. The client MAY choose to sample fewer tokens than requested. + * @param ModelPreferences|null $preferences The server's preferences for which model to select. The client MAY ignore these preferences. + * @param string|null $systemPrompt An optional system prompt the server wants to use for sampling. The client MAY modify or omit this prompt. + */ + public static function make(string|int $id, array $messages, int $maxTokens, ?ModelPreferences $preferences = null, ?string $systemPrompt = null, ?string $includeContext = null, ?float $temperature = null, ?array $stopSequences = null, ?array $metadata = null, ?array $_meta = null): static + { + return new static($id, $messages, $maxTokens, $preferences, $systemPrompt, $includeContext, $temperature, $stopSequences, $metadata, $_meta); + } +} diff --git a/vendor/php-mcp/schema/src/Request/GetPromptRequest.php b/vendor/php-mcp/schema/src/Request/GetPromptRequest.php new file mode 100644 index 000000000..7ff06d82a --- /dev/null +++ b/vendor/php-mcp/schema/src/Request/GetPromptRequest.php @@ -0,0 +1,71 @@ +|null $arguments The arguments to pass to the prompt. + * @param array|null $_meta Optional metadata to include in the request. + */ + public function __construct( + string|int $id, + public readonly string $name, + public readonly ?array $arguments = null, + public readonly ?array $_meta = null + ) { + $params = ['name' => $name]; + + if ($_meta !== null) { + $params['_meta'] = $_meta; + } + + if ($arguments !== null) { + $params['arguments'] = (object) $arguments; + } + + parent::__construct(Constants::JSONRPC_VERSION, $id, 'prompts/get', $params); + } + + /** + * @param string|int $id The ID of the request to cancel. + * @param string $name The name of the prompt to get. + * @param array|null $arguments The arguments to pass to the prompt. + * @param array|null $_meta Optional metadata to include in the request. + */ + public static function make(string|int $id, string $name, ?array $arguments = null, ?array $_meta = null): static + { + return new static($id, $name, $arguments, $_meta); + } + + public static function fromRequest(Request $request): static + { + if ($request->method !== 'prompts/get') { + throw new \InvalidArgumentException('Request is not a prompts/get request'); + } + + $params = $request->params; + + if (! isset($params['name']) || ! is_string($params['name']) || empty($params['name'])) { + throw new \InvalidArgumentException("Missing or invalid 'name' parameter for prompts/get."); + } + + $arguments = $params['arguments'] ?? new \stdClass(); + if (! is_array($arguments) && ! $arguments instanceof \stdClass) { + throw new \InvalidArgumentException("Parameter 'arguments' must be an object/array for prompts/get."); + } + + + return new static($request->id, $params['name'], $arguments, $params['_meta'] ?? null); + } +} diff --git a/vendor/php-mcp/schema/src/Request/InitializeRequest.php b/vendor/php-mcp/schema/src/Request/InitializeRequest.php new file mode 100644 index 000000000..8a2c42a85 --- /dev/null +++ b/vendor/php-mcp/schema/src/Request/InitializeRequest.php @@ -0,0 +1,81 @@ + $this->protocolVersion, + 'capabilities' => $this->capabilities->toArray(), + 'clientInfo' => $this->clientInfo->toArray(), + ]; + + if ($this->_meta !== null) { + $params['_meta'] = $this->_meta; + } + + parent::__construct(Constants::JSONRPC_VERSION, $id, 'initialize', $params); + } + + /** + * @param string|int $id The ID of the request to cancel. + * @param string $protocolVersion The latest version of the Model Context Protocol that the client supports. The client MAY decide to support older versions as well. + * @param ClientCapabilities $capabilities The capabilities of the client. + * @param Implementation $clientInfo Information about the client. + * @param array|null $_meta Optional metadata to include in the request. + */ + public static function make(string|int $id, string $protocolVersion, ClientCapabilities $capabilities, Implementation $clientInfo, ?array $_meta = null): static + { + return new static($id, $protocolVersion, $capabilities, $clientInfo, $_meta); + } + + public static function fromRequest(Request $request): static + { + if ($request->method !== 'initialize') { + throw new \InvalidArgumentException('Request is not an initialize request'); + } + + $params = $request->params; + + if (! isset($params['protocolVersion'])) { + throw new \InvalidArgumentException('protocolVersion is required'); + } + + if (! isset($params['capabilities'])) { + throw new \InvalidArgumentException('capabilities is required'); + } + $capabilities = ClientCapabilities::fromArray($params['capabilities']); + + + if (! isset($params['clientInfo'])) { + throw new \InvalidArgumentException('clientInfo is required'); + } + $clientInfo = Implementation::fromArray($params['clientInfo']); + + return new static($request->id, $params['protocolVersion'], $capabilities, $clientInfo, $params['_meta'] ?? null); + } +} diff --git a/vendor/php-mcp/schema/src/Request/ListPromptsRequest.php b/vendor/php-mcp/schema/src/Request/ListPromptsRequest.php new file mode 100644 index 000000000..1ce76887d --- /dev/null +++ b/vendor/php-mcp/schema/src/Request/ListPromptsRequest.php @@ -0,0 +1,56 @@ +method !== 'prompts/list') { + throw new \InvalidArgumentException('Request is not a prompts/list request'); + } + + return new static($request->id, $request->params['cursor'] ?? null, $request->params['_meta'] ?? null); + } +} diff --git a/vendor/php-mcp/schema/src/Request/ListResourceTemplatesRequest.php b/vendor/php-mcp/schema/src/Request/ListResourceTemplatesRequest.php new file mode 100644 index 000000000..20911f093 --- /dev/null +++ b/vendor/php-mcp/schema/src/Request/ListResourceTemplatesRequest.php @@ -0,0 +1,58 @@ +method !== 'resources/templates/list') { + throw new \InvalidArgumentException('Request is not a list resource templates request'); + } + + return new static($request->id, $request->params['cursor'] ?? null, $request->params['_meta'] ?? null); + } +} diff --git a/vendor/php-mcp/schema/src/Request/ListResourcesRequest.php b/vendor/php-mcp/schema/src/Request/ListResourcesRequest.php new file mode 100644 index 000000000..9de17879c --- /dev/null +++ b/vendor/php-mcp/schema/src/Request/ListResourcesRequest.php @@ -0,0 +1,58 @@ +method !== 'resources/list') { + throw new \InvalidArgumentException('Request is not a list resources request'); + } + + return new static($request->id, $request->params['cursor'] ?? null, $request->params['_meta'] ?? null); + } +} diff --git a/vendor/php-mcp/schema/src/Request/ListRootsRequest.php b/vendor/php-mcp/schema/src/Request/ListRootsRequest.php new file mode 100644 index 000000000..5b558add6 --- /dev/null +++ b/vendor/php-mcp/schema/src/Request/ListRootsRequest.php @@ -0,0 +1,41 @@ +method !== 'tools/list') { + throw new \InvalidArgumentException('Request is not a list tools request'); + } + + return new static($request->id, $request->params['cursor'] ?? null, $request->params['_meta'] ?? null); + } +} diff --git a/vendor/php-mcp/schema/src/Request/PingRequest.php b/vendor/php-mcp/schema/src/Request/PingRequest.php new file mode 100644 index 000000000..8076eea85 --- /dev/null +++ b/vendor/php-mcp/schema/src/Request/PingRequest.php @@ -0,0 +1,40 @@ +method !== 'ping') { + throw new \InvalidArgumentException('Request is not a ping request'); + } + + return new static($request->id, $request->params['_meta'] ?? null); + } +} diff --git a/vendor/php-mcp/schema/src/Request/ReadResourceRequest.php b/vendor/php-mcp/schema/src/Request/ReadResourceRequest.php new file mode 100644 index 000000000..55a438d10 --- /dev/null +++ b/vendor/php-mcp/schema/src/Request/ReadResourceRequest.php @@ -0,0 +1,59 @@ + $uri, + ]; + + if ($_meta !== null) { + $params['_meta'] = $_meta; + } + + parent::__construct(Constants::JSONRPC_VERSION, $id, 'resources/read', $params); + } + + /** + * @param string|int $id The ID of the request to cancel. + * @param string $uri The URI of the resource to read. + * @param array|null $_meta Optional metadata to include in the request. + */ + public static function make(string|int $id, string $uri, ?array $_meta = null): static + { + return new static($id, $uri, $_meta); + } + + public static function fromRequest(Request $request): static + { + if ($request->method !== 'resources/read') { + throw new \InvalidArgumentException('Request is not a read resource request'); + } + + $params = $request->params; + + if (! isset($params['uri']) || ! is_string($params['uri']) || empty($params['uri'])) { + throw new \InvalidArgumentException("Missing or invalid 'uri' parameter for resources/read."); + } + + return new static($request->id, $params['uri'], $params['_meta'] ?? null); + } +} diff --git a/vendor/php-mcp/schema/src/Request/ResourceSubscribeRequest.php b/vendor/php-mcp/schema/src/Request/ResourceSubscribeRequest.php new file mode 100644 index 000000000..2fd781f5d --- /dev/null +++ b/vendor/php-mcp/schema/src/Request/ResourceSubscribeRequest.php @@ -0,0 +1,56 @@ + $uri]; + if ($_meta !== null) { + $params['_meta'] = $_meta; + } + + parent::__construct(Constants::JSONRPC_VERSION, $id, 'resources/subscribe', $params); + } + + /** + * @param string|int $id The ID of the request to cancel. + * @param string $uri The URI of the resource to subscribe to. + * @param array|null $_meta Optional metadata to include in the request. + */ + public static function make(string|int $id, string $uri, ?array $_meta = null): static + { + return new static($id, $uri, $_meta); + } + + public static function fromRequest(Request $request): static + { + if ($request->method !== 'resources/subscribe') { + throw new \InvalidArgumentException('Request is not a resource subscribe request'); + } + + $params = $request->params; + + if (! isset($params['uri']) || ! is_string($params['uri']) || empty($params['uri'])) { + throw new \InvalidArgumentException("Missing or invalid 'uri' parameter for resources/subscribe."); + } + + return new static($request->id, $params['uri'], $params['_meta'] ?? null); + } +} diff --git a/vendor/php-mcp/schema/src/Request/ResourceUnsubscribeRequest.php b/vendor/php-mcp/schema/src/Request/ResourceUnsubscribeRequest.php new file mode 100644 index 000000000..9d7d2f923 --- /dev/null +++ b/vendor/php-mcp/schema/src/Request/ResourceUnsubscribeRequest.php @@ -0,0 +1,56 @@ + $uri]; + if ($_meta !== null) { + $params['_meta'] = $_meta; + } + + parent::__construct(Constants::JSONRPC_VERSION, $id, 'resources/unsubscribe', $params); + } + + /** + * @param string|int $id The ID of the request to cancel. + * @param string $uri The URI of the resource to unsubscribe from. + * @param array|null $_meta Optional metadata to include in the request. + */ + public static function make(string|int $id, string $uri, ?array $_meta = null): static + { + return new static($id, $uri, $_meta); + } + + public static function fromRequest(Request $request): static + { + if ($request->method !== 'resources/unsubscribe') { + throw new \InvalidArgumentException('Request is not a resource unsubscribe request'); + } + + $params = $request->params; + + if (! isset($params['uri']) || ! is_string($params['uri']) || empty($params['uri'])) { + throw new \InvalidArgumentException("Missing or invalid 'uri' parameter for resources/unsubscribe."); + } + + return new static($request->id, $params['uri'], $params['_meta'] ?? null); + } +} diff --git a/vendor/php-mcp/schema/src/Request/SetLogLevelRequest.php b/vendor/php-mcp/schema/src/Request/SetLogLevelRequest.php new file mode 100644 index 000000000..2197e5746 --- /dev/null +++ b/vendor/php-mcp/schema/src/Request/SetLogLevelRequest.php @@ -0,0 +1,60 @@ + $level->value, + ]; + + if ($_meta !== null) { + $params['_meta'] = $_meta; + } + + parent::__construct(Constants::JSONRPC_VERSION, $id, 'logging/setLevel', $params); + } + + /** + * @param string|int $id The ID of the request to cancel. + * @param LoggingLevel $level The level of logging that the client wants to receive from the server. The server should send all logs at this level and higher (i.e., more severe) to the client as notifications/message. + * @param array|null $_meta Optional metadata to include in the request. + */ + public static function make(string|int $id, LoggingLevel $level, ?array $_meta = null): static + { + return new static($id, $level, $_meta); + } + + public static function fromRequest(Request $request): static + { + if ($request->method !== 'logging/setLevel') { + throw new \InvalidArgumentException('Request is not a logging/setLevel request'); + } + + $params = $request->params; + + if (! isset($params['level']) || ! is_string($params['level']) || empty($params['level'])) { + throw new \InvalidArgumentException("Missing or invalid 'level' parameter for logging/setLevel."); + } + + return new static($request->id, LoggingLevel::from($params['level']), $params['_meta'] ?? null); + } +} diff --git a/vendor/php-mcp/schema/src/Resource.php b/vendor/php-mcp/schema/src/Resource.php new file mode 100644 index 000000000..076401ae0 --- /dev/null +++ b/vendor/php-mcp/schema/src/Resource.php @@ -0,0 +1,109 @@ + $this->uri, + 'name' => $this->name, + ]; + if ($this->description !== null) { + $data['description'] = $this->description; + } + if ($this->mimeType !== null) { + $data['mimeType'] = $this->mimeType; + } + if ($this->annotations !== null) { + $data['annotations'] = $this->annotations->toArray(); + } + if ($this->size !== null) { + $data['size'] = $this->size; + } + return $data; + } + + public static function fromArray(array $data): static + { + if (empty($data['uri']) || !is_string($data['uri'])) { + throw new \InvalidArgumentException("Invalid or missing 'uri' in Resource data."); + } + if (empty($data['name']) || !is_string($data['name'])) { + throw new \InvalidArgumentException("Invalid or missing 'name' in Resource data."); + } + return new static( + uri: $data['uri'], + name: $data['name'], + description: $data['description'] ?? null, + mimeType: $data['mimeType'] ?? null, + annotations: isset($data['annotations']) ? Annotations::fromArray($data['annotations']) : null, + size: isset($data['size']) ? (int)$data['size'] : null + ); + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/php-mcp/schema/src/ResourceReference.php b/vendor/php-mcp/schema/src/ResourceReference.php new file mode 100644 index 000000000..330667452 --- /dev/null +++ b/vendor/php-mcp/schema/src/ResourceReference.php @@ -0,0 +1,40 @@ + $this->type, + 'uri' => $this->uri, + ]; + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/php-mcp/schema/src/ResourceTemplate.php b/vendor/php-mcp/schema/src/ResourceTemplate.php new file mode 100644 index 000000000..ae25a10c1 --- /dev/null +++ b/vendor/php-mcp/schema/src/ResourceTemplate.php @@ -0,0 +1,98 @@ + $this->uriTemplate, + 'name' => $this->name, + ]; + if ($this->description !== null) { + $data['description'] = $this->description; + } + if ($this->mimeType !== null) { + $data['mimeType'] = $this->mimeType; + } + if ($this->annotations !== null) { + $data['annotations'] = $this->annotations->toArray(); + } + return $data; + } + + public static function fromArray(array $data): static + { + if (empty($data['uriTemplate']) || !is_string($data['uriTemplate'])) { + throw new \InvalidArgumentException("Invalid or missing 'uriTemplate' in ResourceTemplate data."); + } + if (empty($data['name']) || !is_string($data['name'])) { + throw new \InvalidArgumentException("Invalid or missing 'name' in ResourceTemplate data."); + } + return new static( + uriTemplate: $data['uriTemplate'], + name: $data['name'], + description: $data['description'] ?? null, + mimeType: $data['mimeType'] ?? null, + annotations: isset($data['annotations']) ? Annotations::fromArray($data['annotations']) : null + ); + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/php-mcp/schema/src/Result/CallToolResult.php b/vendor/php-mcp/schema/src/Result/CallToolResult.php new file mode 100644 index 000000000..7b3c9485f --- /dev/null +++ b/vendor/php-mcp/schema/src/Result/CallToolResult.php @@ -0,0 +1,117 @@ + $content The content of the tool result + * @param bool $isError Whether the tool execution resulted in an error. If not set, this is assumed to be false (the call was successful). + */ + public function __construct( + public readonly array $content, + public readonly bool $isError = false + ) { + foreach ($this->content as $item) { + if (!$item instanceof Content) { + throw new \InvalidArgumentException('Content must be an array of Content objects.'); + } + } + } + + /** + * Create a new CallToolResult. + * + * @param array $content The content of the tool result + * @param bool $isError Whether the tool execution resulted in an error + */ + public static function make(array $content, bool $isError = false): static + { + return new static($content, $isError); + } + + /** + * Create a new CallToolResult with success status. + * + * @param array $content The content of the tool result + */ + public static function success(array $content): static + { + return new static($content, false); + } + + /** + * Create a new CallToolResult with error status. + * + * @param array $content The content of the tool result + */ + public static function error(array $content): static + { + return new static($content, true); + } + + /** + * Convert the result to an array. + */ + public function toArray(): array + { + return [ + 'content' => array_map(fn($item) => $item->toArray(), $this->content), + 'isError' => $this->isError, + ]; + } + + public static function fromArray(array $data): static + { + if (!isset($data['content']) || !is_array($data['content'])) { + throw new \InvalidArgumentException("Missing or invalid 'content' array in CallToolResult data."); + } + + $contents = []; + + foreach ($data['content'] as $item) { + $contents[] = match ($item['type'] ?? null) { + 'text' => TextContent::fromArray($item), + 'image' => ImageContent::fromArray($item), + 'audio' => AudioContent::fromArray($item), + 'resource' => EmbeddedResource::fromArray($item), + null => throw new \InvalidArgumentException("Missing 'type' in CallToolResult content item."), + default => throw new \InvalidArgumentException("Invalid content type in CallToolResult data: {$item['type']}"), + }; + } + + return new static( + $contents, + $data['isError'] ?? false + ); + } + + public static function fromResponse(Response $response): static + { + return self::fromArray($response->result); + } +} diff --git a/vendor/php-mcp/schema/src/Result/CompletionCompleteResult.php b/vendor/php-mcp/schema/src/Result/CompletionCompleteResult.php new file mode 100644 index 000000000..e5c1f79d7 --- /dev/null +++ b/vendor/php-mcp/schema/src/Result/CompletionCompleteResult.php @@ -0,0 +1,56 @@ +values) > 100) { + throw new \InvalidArgumentException('Values must not exceed 100 items'); + } + } + + public function toArray(): array + { + $completion = [ + 'values' => $this->values, + ]; + + if ($this->total !== null) { + $completion['total'] = $this->total; + } + if ($this->hasMore !== null) { + $completion['hasMore'] = $this->hasMore; + } + + return ['completion' => $completion]; + } + + /** + * Create a new CompletionCompleteResult. + * + * @param string[] $values An array of completion values. Must not exceed 100 items. + * @param int|null $total The total number of completion options available. This can exceed the number of values actually sent in the response. + * @param bool|null $hasMore Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown. + */ + public static function make(array $values, ?int $total = null, ?bool $hasMore = null): static + { + return new static($values, $total, $hasMore); + } +} diff --git a/vendor/php-mcp/schema/src/Result/CreateSamplingMesssageResult.php b/vendor/php-mcp/schema/src/Result/CreateSamplingMesssageResult.php new file mode 100644 index 000000000..ad41dec5c --- /dev/null +++ b/vendor/php-mcp/schema/src/Result/CreateSamplingMesssageResult.php @@ -0,0 +1,56 @@ + $this->role->value, + 'content' => $this->content->toArray(), + 'model' => $this->model + ]; + + if ($this->stopReason !== null) { + $result['stopReason'] = $this->stopReason; + } + return $result; + } +} diff --git a/vendor/php-mcp/schema/src/Result/EmptyResult.php b/vendor/php-mcp/schema/src/Result/EmptyResult.php new file mode 100644 index 000000000..c490c9478 --- /dev/null +++ b/vendor/php-mcp/schema/src/Result/EmptyResult.php @@ -0,0 +1,41 @@ +messages as $message) { + if (!$message instanceof PromptMessage) { + throw new \InvalidArgumentException('Messages must be an array of PromptMessage objects.'); + } + } + } + + /** + * Create a new GetPromptResult. + * + * @param PromptMessage[] $messages The messages in the prompt + * @param string|null $description Optional description of the prompt + */ + public static function make(array $messages, ?string $description = null): static + { + return new static($messages, $description); + } + + /** + * Convert the result to an array. + */ + public function toArray(): array + { + $result = [ + 'messages' => array_map(fn($message) => $message->toArray(), $this->messages), + ]; + + if ($this->description !== null) { + $result['description'] = $this->description; + } + + return $result; + } + + public static function fromArray(array $data): static + { + if (!isset($data['messages']) || !is_array($data['messages'])) { + throw new \InvalidArgumentException("Missing or invalid 'messages' array in GetPromptResult data."); + } + + $messages = []; + foreach ($data['messages'] as $message) { + $messages[] = PromptMessage::fromArray($message); + } + + return new static( + $messages, + $data['description'] ?? null + ); + } + + public static function fromResponse(Response $response): static + { + return self::fromArray($response->result); + } +} diff --git a/vendor/php-mcp/schema/src/Result/InitializeResult.php b/vendor/php-mcp/schema/src/Result/InitializeResult.php new file mode 100644 index 000000000..b091b29af --- /dev/null +++ b/vendor/php-mcp/schema/src/Result/InitializeResult.php @@ -0,0 +1,94 @@ +|null $_meta Optional _meta field. + */ + public function __construct( + public readonly string $protocolVersion, + public readonly ServerCapabilities $capabilities, + public readonly Implementation $serverInfo, + public readonly ?string $instructions = null, + public readonly ?array $_meta = null + ) {} + + /** + * Convert the result to an array. + * + * @return array{protocolVersion: string, capabilities: array, serverInfo: array, instructions?: string, _meta?: array} + */ + public function toArray(): array + { + $data = [ + 'protocolVersion' => $this->protocolVersion, + 'capabilities' => $this->capabilities->toArray(), + 'serverInfo' => $this->serverInfo->toArray(), + ]; + if ($this->instructions !== null) { + $data['instructions'] = $this->instructions; + } + if ($this->_meta !== null) { + $data['_meta'] = $this->_meta; + } + return $data; + } + + /** + * Create a new InitializeResult. + * + * @param string $protocolVersion The version of the Model Context Protocol that the server wants to use. This may not match the version that the client requested. If the client cannot support this version, it MUST disconnect. + * @param ServerCapabilities $capabilities The capabilities of the server. + * @param Implementation $serverInfo Information about the server. + * @param string|null $instructions Instructions describing how to use the server and its features. This can be used by clients to improve the LLM's understanding of available tools, resources, etc. It can be thought of like a "hint" to the model. For example, this information MAY be added to the system prompt. + * @param array|null $_meta Optional _meta field. + */ + public static function make(string $protocolVersion, ServerCapabilities $capabilities, Implementation $serverInfo, ?string $instructions = null, ?array $_meta = null): static + { + return new static($protocolVersion, $capabilities, $serverInfo, $instructions, $_meta); + } + + public static function fromArray(array $data): static + { + if (!isset($data['protocolVersion']) || !is_string($data['protocolVersion'])) { + throw new \InvalidArgumentException("Missing or invalid 'protocolVersion'"); + } + if (!isset($data['capabilities']) || !is_array($data['capabilities'])) { + throw new \InvalidArgumentException("Missing or invalid 'capabilities'"); + } + if (!isset($data['serverInfo']) || !is_array($data['serverInfo'])) { + throw new \InvalidArgumentException("Missing or invalid 'serverInfo'"); + } + + return new static( + $data['protocolVersion'], + ServerCapabilities::fromArray($data['capabilities']), + Implementation::fromArray($data['serverInfo']), + $data['instructions'] ?? null, + $data['_meta'] ?? null + ); + } + + public static function fromResponse(Response $response): static + { + return self::fromArray($response->result); + } +} diff --git a/vendor/php-mcp/schema/src/Result/ListPromptsResult.php b/vendor/php-mcp/schema/src/Result/ListPromptsResult.php new file mode 100644 index 000000000..43264c318 --- /dev/null +++ b/vendor/php-mcp/schema/src/Result/ListPromptsResult.php @@ -0,0 +1,67 @@ + $prompts The list of prompt definitions. + * @param string|null $nextCursor An opaque token representing the pagination position after the last returned result. + * + * If present, there may be more results available. + */ + public function __construct( + public readonly array $prompts, + public readonly ?string $nextCursor = null + ) {} + + /** + * Create a new ListPromptsResult. + * + * @param array $prompts The list of prompt definitions. + * @param string|null $nextCursor The cursor for the next page, or null if this is the last page. + */ + public static function make(array $prompts, ?string $nextCursor = null): static + { + return new static($prompts, $nextCursor); + } + + public function toArray(): array + { + $result = [ + 'prompts' => array_map(fn(Prompt $p) => $p->toArray(), $this->prompts), + ]; + + if ($this->nextCursor) { + $result['nextCursor'] = $this->nextCursor; + } + + return $result; + } + + public static function fromArray(array $data): static + { + if (!isset($data['prompts']) || !is_array($data['prompts'])) { + throw new \InvalidArgumentException("Missing or invalid 'prompts' array in ListPromptsResult data."); + } + + return new static( + array_map(fn(array $prompt) => Prompt::fromArray($prompt), $data['prompts']), + $data['nextCursor'] ?? null + ); + } + + public static function fromResponse(Response $response): static + { + return self::fromArray($response->result); + } +} diff --git a/vendor/php-mcp/schema/src/Result/ListResourceTemplatesResult.php b/vendor/php-mcp/schema/src/Result/ListResourceTemplatesResult.php new file mode 100644 index 000000000..85f984ace --- /dev/null +++ b/vendor/php-mcp/schema/src/Result/ListResourceTemplatesResult.php @@ -0,0 +1,70 @@ + $resourceTemplates The list of resource template definitions. + * @param string|null $nextCursor An opaque token representing the pagination position after the last returned result. + * + * If present, there may be more results available. + */ + public function __construct( + public readonly array $resourceTemplates, + public readonly ?string $nextCursor = null + ) {} + + /** + * Create a new ListResourceTemplatesResult. + * + * @param array $resourceTemplates The list of resource template definitions. + * @param string|null $nextCursor The cursor for the next page, or null if this is the last page. + */ + public static function make(array $resourceTemplates, ?string $nextCursor = null): static + { + return new static($resourceTemplates, $nextCursor); + } + + /** + * Convert the result to an array. + */ + public function toArray(): array + { + $result = [ + 'resourceTemplates' => array_map(fn(ResourceTemplate $t) => $t->toArray(), $this->resourceTemplates), + ]; + + if ($this->nextCursor) { + $result['nextCursor'] = $this->nextCursor; + } + + return $result; + } + + public static function fromArray(array $data): static + { + if (!isset($data['resourceTemplates']) || !is_array($data['resourceTemplates'])) { + throw new \InvalidArgumentException("Missing or invalid 'resourceTemplates' array in ListResourceTemplatesResult data."); + } + + return new static( + array_map(fn(array $resourceTemplate) => ResourceTemplate::fromArray($resourceTemplate), $data['resourceTemplates']), + $data['nextCursor'] ?? null + ); + } + + public static function fromResponse(Response $response): static + { + return self::fromArray($response->result); + } +} diff --git a/vendor/php-mcp/schema/src/Result/ListResourcesResult.php b/vendor/php-mcp/schema/src/Result/ListResourcesResult.php new file mode 100644 index 000000000..263ff896e --- /dev/null +++ b/vendor/php-mcp/schema/src/Result/ListResourcesResult.php @@ -0,0 +1,70 @@ + $resources The list of resource definitions. + * @param string|null $nextCursor An opaque token representing the pagination position after the last returned result. + * + * If present, there may be more results available. + */ + public function __construct( + public readonly array $resources, + public readonly ?string $nextCursor = null + ) {} + + /** + * Create a new ListResourcesResult. + * + * @param array $resources The list of resource definitions. + * @param string|null $nextCursor The cursor for the next page, or null if this is the last page. + */ + public static function make(array $resources, ?string $nextCursor = null): static + { + return new static($resources, $nextCursor); + } + + /** + * Convert the result to an array. + */ + public function toArray(): array + { + $result = [ + 'resources' => array_map(fn(Resource $r) => $r->toArray(), $this->resources), + ]; + + if ($this->nextCursor !== null) { + $result['nextCursor'] = $this->nextCursor; + } + + return $result; + } + + public static function fromArray(array $data): static + { + if (!isset($data['resources']) || !is_array($data['resources'])) { + throw new \InvalidArgumentException("Missing or invalid 'resources' array in ListResourcesResult data."); + } + + return new static( + array_map(fn(array $resource) => Resource::fromArray($resource), $data['resources']), + $data['nextCursor'] ?? null + ); + } + + public static function fromResponse(Response $response): static + { + return self::fromArray($response->result); + } +} diff --git a/vendor/php-mcp/schema/src/Result/ListRootsResult.php b/vendor/php-mcp/schema/src/Result/ListRootsResult.php new file mode 100644 index 000000000..453893d3e --- /dev/null +++ b/vendor/php-mcp/schema/src/Result/ListRootsResult.php @@ -0,0 +1,49 @@ + $this->roots, + ]; + + if ($this->_meta !== null) { + $result['_meta'] = $this->_meta; + } + + return $result; + } +} diff --git a/vendor/php-mcp/schema/src/Result/ListToolsResult.php b/vendor/php-mcp/schema/src/Result/ListToolsResult.php new file mode 100644 index 000000000..86c170def --- /dev/null +++ b/vendor/php-mcp/schema/src/Result/ListToolsResult.php @@ -0,0 +1,67 @@ + $tools The list of tool definitions. + * @param string|null $nextCursor An opaque token representing the pagination position after the last returned result. + * + * If present, there may be more results available. + */ + public function __construct( + public readonly array $tools, + public readonly ?string $nextCursor = null + ) {} + + /** + * Create a new ListToolsResult. + * + * @param array $tools The list of tool definitions. + * @param string|null $nextCursor The cursor for the next page, or null if this is the last page. + */ + public static function make(array $tools, ?string $nextCursor = null): static + { + return new static($tools, $nextCursor); + } + + public function toArray(): array + { + $result = [ + 'tools' => array_map(fn(Tool $t) => $t->toArray(), $this->tools), + ]; + + if ($this->nextCursor) { + $result['nextCursor'] = $this->nextCursor; + } + + return $result; + } + + public static function fromArray(array $data): static + { + if (!isset($data['tools']) || !is_array($data['tools'])) { + throw new \InvalidArgumentException("Missing or invalid 'tools' array in ListToolsResult data."); + } + + return new static( + array_map(fn(array $tool) => Tool::fromArray($tool), $data['tools']), + $data['nextCursor'] ?? null + ); + } + + public static function fromResponse(Response $response): static + { + return self::fromArray($response->result); + } +} diff --git a/vendor/php-mcp/schema/src/Result/ReadResourceResult.php b/vendor/php-mcp/schema/src/Result/ReadResourceResult.php new file mode 100644 index 000000000..e3f1b9d0b --- /dev/null +++ b/vendor/php-mcp/schema/src/Result/ReadResourceResult.php @@ -0,0 +1,71 @@ + $contents The contents of the resource + */ + public function __construct( + public readonly array $contents + ) {} + + /** + * @param array $contents The contents of the resource + */ + public static function make(array $contents): static + { + return new static($contents); + } + + + /** + * Convert the result to an array. + */ + public function toArray(): array + { + return [ + 'contents' => array_map(fn($resource) => $resource->toArray(), $this->contents), + ]; + } + + public static function fromArray(array $data): static + { + if (!isset($data['contents']) || !is_array($data['contents'])) { + throw new \InvalidArgumentException("Missing or invalid 'contents' array in ReadResourceResult data."); + } + + $contents = []; + foreach ($data['contents'] as $content) { + if (isset($content['text'])) { + $contents[] = TextResourceContents::fromArray($content); + } else if (isset($content['blob'])) { + $contents[] = BlobResourceContents::fromArray($content); + } else { + throw new \InvalidArgumentException("Invalid content type in ReadResourceResult data: " . json_encode($content)); + } + } + + return new static( + $contents, + ); + } + + public static function fromResponse(Response $response): static + { + return self::fromArray($response->result); + } +} diff --git a/vendor/php-mcp/schema/src/Root.php b/vendor/php-mcp/schema/src/Root.php new file mode 100644 index 000000000..8e04555ab --- /dev/null +++ b/vendor/php-mcp/schema/src/Root.php @@ -0,0 +1,75 @@ +uri)) { + throw new \InvalidArgumentException("Root URI must start with 'file://'. Given: " . $this->uri); + } + } + + /** + * @param string $uri The URI identifying the root. This *must* start with file:// for now. + * + * This restriction may be relaxed in future versions of the protocol to allow other URI schemes. + * + * @param string|null $name An optional name for the root. + * + * This can be used to provide a human-readable identifier for the root, which may be useful for + * display purposes or for referencing the root in other parts of the application. + */ + public static function make(string $uri, ?string $name = null): static + { + return new static($uri, $name); + } + + public function toArray(): array + { + $data = ['uri' => $this->uri]; + if ($this->name !== null) { + $data['name'] = $this->name; + } + return $data; + } + + public static function fromArray(array $data): static + { + if (empty($data['uri']) || !is_string($data['uri'])) { + throw new \InvalidArgumentException("Invalid or missing 'uri' in Root data."); + } + + return new static( + uri: $data['uri'], + name: $data['name'] ?? null + ); + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/php-mcp/schema/src/ServerCapabilities.php b/vendor/php-mcp/schema/src/ServerCapabilities.php new file mode 100644 index 000000000..b7bcfd002 --- /dev/null +++ b/vendor/php-mcp/schema/src/ServerCapabilities.php @@ -0,0 +1,181 @@ +logging) { + $data['logging'] = new \stdClass(); + } + if ($this->completions) { + $data['completions'] = new \stdClass(); + } + + if ($this->prompts || $this->promptsListChanged) { + $data['prompts'] = new \stdClass(); + if ($this->promptsListChanged) { + $data['prompts']->listChanged = $this->promptsListChanged; + } + } + + if ($this->resources || $this->resourcesSubscribe || $this->resourcesListChanged) { + $data['resources'] = new \stdClass(); + if ($this->resourcesSubscribe) { + $data['resources']->subscribe = $this->resourcesSubscribe; + } + if ($this->resourcesListChanged) { + $data['resources']->listChanged = $this->resourcesListChanged; + } + } + + if ($this->tools || $this->toolsListChanged) { + $data['tools'] = new \stdClass(); + if ($this->toolsListChanged) { + $data['tools']->listChanged = $this->toolsListChanged; + } + } + + if ($this->experimental) { + $data['experimental'] = (object) $this->experimental; + } + + return $data; + } + + public static function fromArray(array $data): static + { + $loggingEnabled = isset($data['logging']); + $completionsEnabled = isset($data['completions']); + $toolsEnabled = isset($data['tools']); + $promptsEnabled = isset($data['prompts']); + $resourcesEnabled = isset($data['resources']); + + $promptsListChanged = null; + if (isset($data['prompts'])) { + if (is_array($data['prompts']) && array_key_exists('listChanged', $data['prompts'])) { + $promptsListChanged = (bool) $data['prompts']['listChanged']; + } elseif (is_object($data['prompts']) && property_exists($data['prompts'], 'listChanged')) { + $promptsListChanged = (bool) $data['prompts']->listChanged; + } + } + + + $resourcesSubscribe = null; + $resourcesListChanged = null; + if (isset($data['resources'])) { + if (is_array($data['resources']) && array_key_exists('subscribe', $data['resources'])) { + $resourcesSubscribe = (bool) $data['resources']['subscribe']; + } elseif (is_object($data['resources']) && property_exists($data['resources'], 'subscribe')) { + $resourcesSubscribe = (bool) $data['resources']->subscribe; + } + if (is_array($data['resources']) && array_key_exists('listChanged', $data['resources'])) { + $resourcesListChanged = (bool) $data['resources']['listChanged']; + } elseif (is_object($data['resources']) && property_exists($data['resources'], 'listChanged')) { + $resourcesListChanged = (bool) $data['resources']->listChanged; + } + } + + $toolsListChanged = null; + if (isset($data['tools'])) { + if (is_array($data['tools']) && array_key_exists('listChanged', $data['tools'])) { + $toolsListChanged = (bool) $data['tools']['listChanged']; + } elseif (is_object($data['tools']) && property_exists($data['tools'], 'listChanged')) { + $toolsListChanged = (bool) $data['tools']->listChanged; + } + } + + return new static( + tools: $toolsEnabled, + toolsListChanged: $toolsListChanged, + resources: $resourcesEnabled, + resourcesSubscribe: $resourcesSubscribe, + resourcesListChanged: $resourcesListChanged, + prompts: $promptsEnabled, + promptsListChanged: $promptsListChanged, + logging: $loggingEnabled, + completions: $completionsEnabled, + experimental: $data['experimental'] ?? null + ); + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/php-mcp/schema/src/Tool.php b/vendor/php-mcp/schema/src/Tool.php new file mode 100644 index 000000000..ffa3731ae --- /dev/null +++ b/vendor/php-mcp/schema/src/Tool.php @@ -0,0 +1,86 @@ +, required: string[]|null } $inputSchema A JSON Schema object (as a PHP array) defining the expected 'arguments' for the tool. + * @param ToolAnnotations|null $annotations Optional additional tool information. + */ + public function __construct( + public readonly string $name, + public readonly array $inputSchema, + public readonly ?string $description, + public readonly ?ToolAnnotations $annotations + ) { + if (!isset($inputSchema['type']) || $inputSchema['type'] !== 'object') { + throw new \InvalidArgumentException("Tool inputSchema must be a JSON Schema of type 'object'."); + } + } + + /** + * @param string $name The name of the tool. + * @param string|null $description A human-readable description of the tool. + * This can be used by clients to improve the LLM's understanding of available tools. It can be thought of like a "hint" to the model. + * @param array{ type: 'object', properties: array, required: string[]|null } $inputSchema A JSON Schema object (as a PHP array) defining the expected 'arguments' for the tool. + * @param ToolAnnotations|null $annotations Optional additional tool information. + */ + public static function make(string $name, array $inputSchema, ?string $description = null, ?ToolAnnotations $annotations = null): static + { + return new static($name, $inputSchema, $description, $annotations); + } + + public function toArray(): array + { + $data = [ + 'name' => $this->name, + 'inputSchema' => $this->inputSchema, + ]; + if ($this->description !== null) { + $data['description'] = $this->description; + } + if ($this->annotations !== null) { + $data['annotations'] = $this->annotations->toArray(); + } + return $data; + } + + public static function fromArray(array $data): static + { + if (empty($data['name']) || !is_string($data['name'])) { + throw new \InvalidArgumentException("Invalid or missing 'name' in Tool data."); + } + if (!isset($data['inputSchema']) || !is_array($data['inputSchema'])) { + throw new \InvalidArgumentException("Invalid or missing 'inputSchema' in Tool data."); + } + if (!isset($data['inputSchema']['type']) || $data['inputSchema']['type'] !== 'object') { + throw new \InvalidArgumentException("Tool inputSchema must be of type 'object'."); + } + if (isset($data['inputSchema']['properties']) && is_array($data['inputSchema']['properties']) && empty($data['inputSchema']['properties'])) { + $data['inputSchema']['properties'] = new \stdClass(); + } + + return new static( + name: $data['name'], + description: isset($data['description']) && is_string($data['description']) ? $data['description'] : null, + inputSchema: $data['inputSchema'], + annotations: isset($data['annotations']) && is_array($data['annotations']) ? ToolAnnotations::fromArray($data['annotations']) : null + ); + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/php-mcp/schema/src/ToolAnnotations.php b/vendor/php-mcp/schema/src/ToolAnnotations.php new file mode 100644 index 000000000..ad8f6539e --- /dev/null +++ b/vendor/php-mcp/schema/src/ToolAnnotations.php @@ -0,0 +1,81 @@ +title !== null) { + $data['title'] = $this->title; + } + if ($this->readOnlyHint !== null) { + $data['readOnlyHint'] = $this->readOnlyHint; + } + if ($this->destructiveHint !== null) { + $data['destructiveHint'] = $this->destructiveHint; + } + if ($this->idempotentHint !== null) { + $data['idempotentHint'] = $this->idempotentHint; + } + if ($this->openWorldHint !== null) { + $data['openWorldHint'] = $this->openWorldHint; + } + return $data; + } + + public static function fromArray(array $data): static + { + return new static( + $data['title'] ?? null, + $data['readOnlyHint'] ?? null, + $data['destructiveHint'] ?? null, + $data['idempotentHint'] ?? null, + $data['openWorldHint'] ?? null + ); + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/php-mcp/server/.github/workflows/changelog.yml b/vendor/php-mcp/server/.github/workflows/changelog.yml new file mode 100644 index 000000000..47d29170c --- /dev/null +++ b/vendor/php-mcp/server/.github/workflows/changelog.yml @@ -0,0 +1,31 @@ +name: "Update Changelog" + +on: + release: + types: [released] + +permissions: + contents: write + +jobs: + update: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: main + + - name: Update Changelog + uses: stefanzweifel/changelog-updater-action@v1 + with: + latest-version: ${{ github.event.release.name }} + release-notes: ${{ github.event.release.body }} + + - name: Commit updated CHANGELOG + uses: stefanzweifel/git-auto-commit-action@v5 + with: + branch: main + commit_message: Update CHANGELOG + file_pattern: CHANGELOG.md \ No newline at end of file diff --git a/vendor/php-mcp/server/.github/workflows/tests.yml b/vendor/php-mcp/server/.github/workflows/tests.yml new file mode 100644 index 000000000..f829a0b7d --- /dev/null +++ b/vendor/php-mcp/server/.github/workflows/tests.yml @@ -0,0 +1,38 @@ +name: Tests + +on: ['push', 'pull_request'] + +jobs: + ci: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + php: [8.1, 8.2, 8.3, 8.4] + max-parallel: 2 + + name: Tests PHP${{ matrix.php }} + + steps: + + - name: Checkout + uses: actions/checkout@v4 + + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ~/.composer/cache/files + key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: none + + - name: Install Composer dependencies + run: composer update --no-interaction --prefer-dist + + - name: Run Tests + run: composer test diff --git a/vendor/php-mcp/server/.gitignore b/vendor/php-mcp/server/.gitignore new file mode 100644 index 000000000..456672017 --- /dev/null +++ b/vendor/php-mcp/server/.gitignore @@ -0,0 +1,67 @@ +# Composer dependencies +/vendor/ +/composer.lock + +# PHPUnit +.phpunit.result.cache + +# PHP CS Fixer +/.php-cs-fixer.cache +/.php-cs-fixer.php + +# Editor directories and files +/.idea +/.vscode +*.sublime-project +*.sublime-workspace + +# Operating system files +.DS_Store +Thumbs.db + +# Local environment files +/.env +/.env.backup +/.env.local + +# PHP CodeSniffer +/.phpcs.xml +/.phpcs.xml.dist +/phpcs.xml +/phpcs.xml.dist + +# PHPStan +/phpstan.neon +/phpstan.neon.dist + +# Local development tools +/.php_cs +/.php_cs.cache +/.php_cs.dist +/_ide_helper.php +/.php-cs-fixer.php + +# Build artifacts +/build/ +/coverage/ + +# PHPUnit coverage reports +/clover.xml +/coverage.xml +/coverage/ + +# Laravel generated files +bootstrap/cache/ +.phpunit.result.cache + +# Local Composer dependencies +composer.phar + +workbench +playground + +# Log files +*.log + +# Cache files +cache \ No newline at end of file diff --git a/vendor/php-mcp/server/CHANGELOG.md b/vendor/php-mcp/server/CHANGELOG.md new file mode 100644 index 000000000..ec8ba4146 --- /dev/null +++ b/vendor/php-mcp/server/CHANGELOG.md @@ -0,0 +1,480 @@ +# Changelog + +All notable changes to `php-mcp/server` will be documented in this file. + +## v3.2.2 - 2025-07-09 + +### What's Changed + +* Fix Architecture graph by @szepeviktor in https://github.com/php-mcp/server/pull/42 +* Fix: Correctly handle invokable class tool handlers by @CodeWithKyrian in https://github.com/php-mcp/server/pull/47 + +### New Contributors + +* @szepeviktor made their first contribution in https://github.com/php-mcp/server/pull/42 + +**Full Changelog**: https://github.com/php-mcp/server/compare/3.2.1...3.2.2 + +## v3.2.1 - 2025-06-30 + +### What's Changed + +* feat: use callable instead of Closure|array|string for handler type by @CodeWithKyrian in https://github.com/php-mcp/server/pull/41 + +**Full Changelog**: https://github.com/php-mcp/server/compare/3.2.0...3.2.1 + +## v3.2.0 - 2025-06-30 + +### What's Changed + +* fix: resolve cache session handler index inconsistencies by @CodeWithKyrian in https://github.com/php-mcp/server/pull/36 +* feat: Add comprehensive callable handler support for closures, static methods, and invokable classes by @CodeWithKyrian in https://github.com/php-mcp/server/pull/38 +* feat: Enhanced Completion Providers with Values and Enum Support by @CodeWithKyrian in https://github.com/php-mcp/server/pull/40 + +### Upgrade Guide + +If you're using the `CompletionProvider` attribute with the named `providerClass` parameter, consider updating to the new `provider` parameter for consistency: + +```php +// Before (still works) +#[CompletionProvider(providerClass: UserProvider::class)] + +// After (recommended) +#[CompletionProvider(provider: UserProvider::class)] + + + +``` +The old `providerClass` parameter continues to work for backward compatibility, but may be dropped in a future major version release. + +**Full Changelog**: https://github.com/php-mcp/server/compare/3.1.1...3.2.0 + +## v3.1.1 - 2025-06-26 + +### What's Changed + +* Fix: implement proper MCP protocol version negotiation by @CodeWithKyrian in https://github.com/php-mcp/server/pull/35 + +**Full Changelog**: https://github.com/php-mcp/server/compare/3.1.0...3.1.1 + +## v3.1.0 - 2025-06-25 + +### What's Changed + +* Refactor: expose session garbage collection method for integration by @CodeWithKyrian in https://github.com/php-mcp/server/pull/31 +* feat: add instructions in server initialization result by @CodeWithKyrian in https://github.com/php-mcp/server/pull/32 +* fix(cache): handle missing session in index for CacheSessionHandler by @CodeWithKyrian in https://github.com/php-mcp/server/pull/33 + +**Full Changelog**: https://github.com/php-mcp/server/compare/3.0.2...3.1.0 + +## v3.0.2 - 2025-06-25 + +### What's Changed + +* fix: Registry cache clearing bug preventing effective caching by @CodeWithKyrian in https://github.com/php-mcp/server/pull/29 +* Fix ServerBuilder error handling for manual element registration by @CodeWithKyrian in https://github.com/php-mcp/server/pull/30 + +**Full Changelog**: https://github.com/php-mcp/server/compare/3.0.1...3.0.2 + +## v3.0.1 - 2025-06-24 + +### What's Changed + +* Fix validation failure for MCP tools without parameters by @CodeWithKyrian in https://github.com/php-mcp/server/pull/28 + +**Full Changelog**: https://github.com/php-mcp/server/compare/3.0.0...3.0.1 + +## v3.0.0 - 2025-06-21 + +This release brings support for the latest MCP protocol version along with enhanced schema generation, new transport capabilities, and streamlined APIs. + +### ✨ New Features + +* **StreamableHttpServerTransport**: New transport with resumability, event sourcing, and JSON response mode for production deployments +* **Smart Schema Generation**: Automatic JSON schema generation from method signatures with optional `#[Schema]` attribute enhancements +* **Completion Providers**: `#[CompletionProvider]` attribute for auto-completion in resource templates and prompts +* **Batch Request Processing**: Full support for JSON-RPC 2.0 batch requests +* **Enhanced Session Management**: Multiple session backends (array, cache, custom) with persistence and garbage collection + +### 🔥 Breaking Changes + +* **Schema Package Integration**: Now uses `php-mcp/schema` package for all DTOs, requests, responses, and content types +* **Session Management**: `ClientStateManager` replaced with `SessionManager` and `Session` classes +* **Component Reorganization**: `Support\*` classes moved to `Utils\*` namespace +* **Request Processing**: `RequestHandler` renamed to `Dispatcher` + +*Note: Most of these changes are internal and won't affect your existing MCP element definitions and handlers.* + +### 🔧 Enhanced Features + +* **Improved Schema System**: The `#[Schema]` attribute can now be used at both method-level and parameter-level (previously parameter-level only) +* **Better Error Handling**: Enhanced JSON-RPC error responses with proper status codes +* **PSR-20 Clock Interface**: Time management with `SystemClock` implementation +* **Event Store Interface**: Pluggable event storage for resumable connections + +### 📦 Dependencies + +* Now requires `php-mcp/schema` ^1.0 +* Enhanced PSR compliance (PSR-3, PSR-11, PSR-16, PSR-20) + +### 🚧 Migration Guide + +#### Capabilities Configuration + +**Before:** + +```php +->withCapabilities(Capabilities::forServer( + resourcesEnabled: true, + promptsEnabled: true, + toolsEnabled: true, + resourceSubscribe: true +)) + + + + + + + + +``` +**After:** + +```php +->withCapabilities(ServerCapabilities::make( + resources: true, + prompts: true, + tools: true, + resourcesSubscribe: true +)) + + + + + + + + +``` +#### Transport Upgrade (Optional) + +For production HTTP deployments, consider upgrading to the new `StreamableHttpServerTransport`: + +**Before:** + +```php +$transport = new HttpServerTransport(host: '127.0.0.1', port: 8080); + + + + + + + + +``` +**After:** + +```php +$transport = new StreamableHttpServerTransport(host: '127.0.0.1', port: 8080); + + + + + + + + +``` +### 📚 Documentation + +* Complete README rewrite with comprehensive examples and deployment guides +* New production deployment section covering VPS, Docker, and SSL setup +* Enhanced schema generation documentation +* Migration guide for v2.x users + +**Full Changelog**: https://github.com/php-mcp/server/compare/2.3.1...3.0.0 + +## v2.3.1 - 2025-06-13 + +### What's Changed + +* Streamline Registry Notifications and Add Discovery Suppression Support by @CodeWithKyrian in https://github.com/php-mcp/server/pull/22 + +**Full Changelog**: https://github.com/php-mcp/server/compare/2.3.0...2.3.1 + +## v2.3.0 - 2025-06-12 + +### What's Changed + +* Fix: Require react/promise ^3.0 for Promise API Compatibility by @CodeWithKyrian in https://github.com/php-mcp/server/pull/18 +* Fix: Correct object serialization in FileCache using serialize/unserialize by @CodeWithKyrian in https://github.com/php-mcp/server/pull/19 +* check the the header X-Forwarded-Proto for scheme by @bangnokia in https://github.com/php-mcp/server/pull/14 +* Feat: Improve HttpServerTransport Extensibility via Protected Methods by @CodeWithKyrian in https://github.com/php-mcp/server/pull/20 + +### New Contributors + +* @bangnokia made their first contribution in https://github.com/php-mcp/server/pull/14 + +**Full Changelog**: https://github.com/php-mcp/server/compare/2.2.1...2.3.0 + +## v2.2.1 - 2025-06-07 + +### What's Changed + +* Fix tool name generation for invokable classes with MCP attributes by @CodeWithKyrian in https://github.com/php-mcp/server/pull/13 + +**Full Changelog**: https://github.com/php-mcp/server/compare/2.2.0...2.2.1 + +## v2.2.0 - 2025-06-03 + +### What's Changed + +* feat(pagination): Added configuration for a server-wide pagination limit, enabling more controlled data retrieval for list-based MCP operations. This limit is utilized by the `RequestProcessor`. +* feat(handlers): Introduced `HandlerResolver` to provide more robust validation and resolution mechanisms for MCP element handlers, improving the reliability of element registration and invocation. +* refactor(server): Modified the server listening mechanism to allow initialization and transport binding without an immediately blocking event loop. This enhances flexibility for embedding the server or managing its lifecycle in diverse application environments. +* refactor(core): Performed general cleanup and enhancements to the internal architecture and dependencies, contributing to improved code maintainability and overall system stability. + +**Full Changelog**: https://github.com/php-mcp/server/compare/2.1.0...2.2.0 + +## v2.1.0 - 2025-05-17 + +### What's Changed + +* feat(schema): add Schema attributes and enhance DocBlock array type parsing by @CodeWithKyrian in https://github.com/php-mcp/server/pull/8 + +**Full Changelog**: https://github.com/php-mcp/server/compare/2.0.1...2.1.0 + +## PHP MCP Server v2.0.1 (HotFix) - 2025-05-11 + +### What's Changed + +* Fix: Ensure react/http is a runtime dependency for HttpServerTransport by @CodeWithKyrian in https://github.com/php-mcp/server/pull/7 + +**Full Changelog**: https://github.com/php-mcp/server/compare/2.0.0...2.0.1 + +## PHP MCP Server v2.0.0 - 2025-05-11 + +This release marks a significant architectural refactoring of the package, aimed at improving modularity, testability, flexibility, and aligning its structure more closely with the `php-mcp/client` library. The core functionality remains, but the way servers are configured, run, and integrated has fundamentally changed. + +### What's Changed + +#### Core Architecture Overhaul + +* **Decoupled Design:** The server core logic is now separated from the transport (network/IO) layer. + + * **`ServerBuilder`:** A new fluent builder (`Server::make()`) is the primary way to configure server identity, dependencies (Logger, Cache, Container, Loop), capabilities, and manually registered elements. + * **`Server` Object:** The main `Server` class, created by the builder, now holds the configured core components (`Registry`, `Processor`, `ClientStateManager`, `Configuration`) but is transport-agnostic itself. + * **`ServerTransportInterface`:** A new event-driven interface defines the contract for server-side transports (Stdio, Http). Transports are now responsible solely for listening and raw data transfer, emitting events for lifecycle and messages. + * **`Protocol`:** A new internal class acts as a bridge, listening to events from a bound `ServerTransportInterface` and coordinating interactions with the `Processor` and `ClientStateManager`. + +* **Explicit Server Execution:** + + * The old `$server->run(?string)` method is **removed**. + * **`$server->listen(ServerTransportInterface $transport)`:** Introduced as the primary way to start a *standalone* server. It binds the `Protocol` to the provided transport, starts the listener, and runs the event loop (making it a blocking call). + + +#### Discovery and Caching Refinements + +* **Explicit Discovery:** Attribute discovery is no longer triggered automatically during `build()`. You must now explicitly call `$server->discover(basePath: ..., scanDirs: ...)` *after* building the server instance if you want to find elements via attributes. + +* **Caching Behavior:** + + * Only *discovered* elements are eligible for caching. Manually registered elements (via `ServerBuilder->with*` methods) are **never cached**. + * The `Registry` attempts to load discovered elements from cache upon instantiation (during `ServerBuilder::build()`). + * Calling `$server->discover()` will first clear any previously discovered/cached elements from the registry before scanning. It then saves the *newly discovered* results to the cache if enabled (`saveToCache: true`). + * `Registry` cache methods renamed for clarity: `saveDiscoveredElementsToCache()` and `clearDiscoveredElements()`. + * `Registry::isLoaded()` renamed to `discoveryRanOrCached()` for better clarity. + +* **Manual vs. Discovered Precedence:** If an element is registered both manually and found via discovery/cache with the same identifier (name/URI), the **manually registered version always takes precedence**. + + +#### Dependency Injection and Configuration + +* **`ConfigurationRepositoryInterface` Removed:** This interface and its default implementation (`ArrayConfigurationRepository`) have been removed. +* **`Configuration` Value Object:** A new `PhpMcp\Server\Configuration` readonly value object bundles core dependencies (Logger, Loop, Cache, Container, Server Info, Capabilities, TTLs) assembled by the `ServerBuilder`. +* **Simplified Dependencies:** Core components (`Registry`, `Processor`, `ClientStateManager`, `DocBlockParser`, `Discoverer`) now have simpler constructors, accepting direct dependencies. +* **PSR-11 Container Role:** The container provided via `ServerBuilder->withContainer()` (or the default `BasicContainer`) is now primarily used by the `Processor` to resolve *user-defined handler classes* and their dependencies. +* **Improved `BasicContainer`:** The default DI container (`PhpMcp\Server\Defaults\BasicContainer`) now supports simple constructor auto-wiring. +* **`ClientStateManager` Default Cache:** If no `CacheInterface` is provided to the `ClientStateManager`, it now defaults to an in-memory `PhpMcp\Server\Defaults\ArrayCache`. + +#### Schema Generation and Validation + +* **Removed Optimistic String Format Inference:** The `SchemaGenerator` no longer automatically infers JSON Schema `format` keywords (like "date-time", "email") for string parameters. This makes default schemas less strict, avoiding validation issues for users with simpler string formats. Specific format validation should now be handled within tool/resource methods or via future explicit schema annotation features. +* **Improved Tool Call Validation Error Messages:** When `tools/call` parameters fail schema validation, the JSON-RPC error response now includes a more informative summary message detailing the specific validation failures, in addition to the structured error data. + +#### Transports + +* **New Implementations:** Introduced `PhpMcp\Server\Transports\StdioServerTransport` and `PhpMcp\Server\Transports\HttpServerTransport`, both implementing `ServerTransportInterface`. + + * `StdioServerTransport` constructor now accepts custom input/output stream resources, improving testability and flexibility (defaults to `STDIN`/`STDOUT`). + * `HttpServerTransport` constructor now accepts an array of request interceptor callables for custom request pre-processing (e.g., authentication), and also takes `host`, `port`, `mcpPathPrefix`, and `sslContext` for server configuration. + +* **Windows `stdio` Limitation:** `StdioServerTransport` now throws a `TransportException` if instantiated with default `STDIN`/`STDOUT` on Windows, due to PHP's limitations with non-blocking pipes, guiding users to `WSL` or `HttpServerTransport`. + +* **Aware Interfaces:** Transports can implement `LoggerAwareInterface` and `LoopAwareInterface` to receive the configured Logger and Loop instances when `$server->listen()` is called. + +* **Removed:** The old `StdioTransportHandler`, `HttpTransportHandler`, and `ReactPhpHttpTransportHandler` classes. + + +#### Capabilities Configuration + +* **`Model\Capabilities` Class:** Introduced a new `PhpMcp\Server\Model\Capabilities` value object (created via `Capabilities::forServer(...)`) to explicitly configure and represent server capabilities. + +#### Exception Handling + +* **`McpServerException`:** Renamed the base exception from `McpException` to `PhpMcp\Server\Exception\McpServerException`. +* **New Exception Types:** Added more specific exceptions: `ConfigurationException`, `DiscoveryException`, `DefinitionException`, `TransportException`, `ProtocolException`. + +#### Fixes + +* Fixed `StdioServerTransport` not cleanly exiting on `Ctrl+C` due to event loop handling. +* Fixed `TypeError` in `JsonRpc\Response` for parse errors with `null` ID. +* Corrected discovery caching logic for explicit `discover()` calls. +* Improved `HttpServerTransport` robustness for initial SSE event delivery and POST body handling. +* Ensured manual registrations correctly take precedence over discovered/cached elements with the same identifier. + +#### Internal Changes + +* Introduced `LoggerAwareInterface` and `LoopAwareInterface` for dependency injection into transports. +* Refined internal event handling between transport implementations and the `Protocol`. +* Renamed `TransportState` to `ClientStateManager` and introduced a `ClientState` Value Object. + +#### Documentation and Examples + +* Significantly revised `README.md` to reflect the new architecture, API, discovery flow, transport usage, and configuration. +* Added new and updated examples for standalone `stdio` and `http` servers, demonstrating discovery, manual registration, custom dependency injection, complex schemas, and environment variable usage. + +### Breaking Changes + +This is a major refactoring with significant breaking changes: + +1. **`Server->run()` Method Removed:** Replace calls to `$server->run('stdio')` with: + + ```php + $transport = new StdioServerTransport(); + // Optionally call $server->discover(...) first + $server->listen($transport); + + + + + + + + + + + + + + + + ``` + The `http` and `reactphp` options for `run()` were already invalid and are fully removed. + +2. **Configuration (`ConfigurationRepositoryInterface` Removed):** Configuration is now handled via the `Configuration` VO assembled by `ServerBuilder`. Remove any usage of the old `ConfigurationRepositoryInterface`. Core settings like server name/version are set via `withServerInfo`, capabilities via `withCapabilities`. + +3. **Dependency Injection:** + + * If using `ServerBuilder->withContainer()` with a custom PSR-11 container, that container is now only responsible for resolving *your application's handler classes* and their dependencies. + * Core server dependencies (Logger, Cache, Loop) **must** be provided explicitly to the `ServerBuilder` using `withLogger()`, `withCache()`, `withLoop()` or rely on the builder's defaults. + +4. **Transport Handlers Replaced:** + + * `StdioTransportHandler`, `HttpTransportHandler`, `ReactPhpHttpTransportHandler` are **removed**. + * Use `new StdioServerTransport()` or `new HttpServerTransport(...)` and pass them to `$server->listen()`. + * Constructor signatures and interaction patterns have changed. + +5. **`Registry` Cache Methods Renamed:** `saveElementsToCache` is now `saveDiscoveredElementsToCache`, and `clearCache` is now `clearDiscoveredElements`. Their behavior is also changed to only affect discovered elements. + +6. **Core Component Constructors:** The constructors for `Registry`, `Processor`, `ClientStateManager` (previously `TransportState`), `Discoverer`, `DocBlockParser` have changed. Update any direct instantiations (though typically these are managed internally). + +7. **Exception Renaming:** `McpException` is now `McpServerException`. Update `catch` blocks accordingly. + +8. **Default Null Logger:** Logging is effectively disabled by default. Provide a logger via `ServerBuilder->withLogger()` to enable it. + +9. **Schema Generation:** Automatic string `format` inference (e.g., "date-time") removed from `SchemaGenerator`. String parameters are now plain strings in the schema unless a more advanced format definition mechanism is used in the future. + + +### Deprecations + +* (None introduced in this refactoring, as major breaking changes were made directly). + +**Full Changelog**: https://github.com/php-mcp/server/compare/1.1.0...2.0.0 + +## PHP MCP Server v1.1.0 - 2025-05-01 + +### Added + +* **Manual Element Registration:** Added fluent methods `withTool()`, `withResource()`, `withPrompt()`, and `withResourceTemplate()` to the `Server` class. This allows programmatic registration of MCP elements as an alternative or supplement to attribute discovery. Both `[ClassName::class, 'methodName']` array handlers and invokable class string handlers are supported. +* **Invokable Class Attribute Discovery:** The server's discovery mechanism now supports placing `#[Mcp*]` attributes directly on invokable PHP class definitions (classes with a public `__invoke` method). The `__invoke` method will be used as the handler. +* **Discovery Path Configuration:** Added `withBasePath()`, `withScanDirectories()`, and `withExcludeDirectories()` methods to the `Server` class for finer control over which directories are scanned during attribute discovery. + +### Changed + +* **Dependency Injection:** Refactored internal dependency management. Core server components (`Processor`, `Registry`, `ClientStateManager`, etc.) now resolve `LoggerInterface`, `CacheInterface`, and `ConfigurationRepositoryInterface` Just-In-Time from the provided PSR-11 container. See **Breaking Changes** for implications. +* **Default Logging Behavior:** Logging is now **disabled by default**. To enable logging, provide a `LoggerInterface` implementation via `withLogger()` (when using the default container) or by registering it within your custom PSR-11 container. +* **Transport Handler Constructors:** Transport Handlers (e.g., `StdioTransportHandler`, `HttpTransportHandler`) now primarily accept the `Server` instance in their constructor, simplifying their instantiation. + +### Fixed + +* Prevented potential "Constant STDERR not defined" errors in non-CLI environments by changing the default logger behavior (see Changed section). + +### Updated + +* Extensively updated `README.md` to document manual registration, invokable class discovery, the dependency injection overhaul, discovery path configuration, transport handler changes, and the new default logging behavior. + +### Breaking Changes + +* **Dependency Injection Responsibility:** Due to the DI refactoring, if you provide a custom PSR-11 container using `withContainer()`, you **MUST** ensure that your container is configured to provide implementations for `LoggerInterface`, `CacheInterface`, and `ConfigurationRepositoryInterface`. The server relies on being able to fetch these from the container. +* **`withLogger/Cache/Config` Behavior with Custom Container:** When a custom container is provided via `withContainer()`, calls to `->withLogger()`, `->withCache()`, or `->withConfig()` on the `Server` instance will **not** override the services resolved from *your* container during runtime. Configuration for these core services must be done directly within your custom container setup. +* **Transport Handler Constructor Signatures:** The constructor signatures for `StdioTransportHandler`, `HttpTransportHandler`, and `ReactPhpHttpTransportHandler` have changed. They now primarily require the `Server` instance. Update any direct instantiations of these handlers accordingly. + +**Full Changelog**: https://github.com/php-mcp/server/compare/1.0.0...1.1.0 + +## Release v1.0.0 - Initial Release + +🚀 **Initial release of PHP MCP SERVER!** + +This release introduces the core implementation of the Model Context Protocol (MCP) server for PHP applications. The goal is to provide a robust, flexible, and developer-friendly way to expose parts of your PHP application as MCP Tools, Resources, and Prompts, enabling standardized communication with AI assistants like Claude, Cursor, and others. + +### ✨ Key Features: + +* **Attribute-Based Definitions:** Easily define MCP Tools (`#[McpTool]`), Resources (`#[McpResource]`, `#[McpResourceTemplate]`), and Prompts (`#[McpPrompt]`) using PHP 8 attributes directly on your methods. + +* **Automatic Metadata Inference:** Leverages method signatures (parameters, type hints) and DocBlocks (`@param`, `@return`, summaries) to automatically generate MCP schemas and descriptions, minimizing boilerplate. + +* **PSR Compliance:** Integrates seamlessly with standard PHP interfaces: + + * `PSR-3` (LoggerInterface) for flexible logging. + * `PSR-11` (ContainerInterface) for dependency injection and class resolution. + * `PSR-16` (SimpleCacheInterface) for caching discovered elements and transport state. + +* **Automatic Discovery:** Scans configured directories to find and register your annotated MCP elements. + +* **Flexible Configuration:** Uses a configuration repository (`ConfigurationRepositoryInterface`) for fine-grained control over server behaviour, capabilities, and caching. + +* **Multiple Transports:** + + * Built-in support for the `stdio` transport, ideal for command-line driven clients. + * Includes `HttpTransportHandler` components for building standard `http` (HTTP+SSE) transports (requires integration into an HTTP server). + * Provides `ReactPhpHttpTransportHandler` for seamless integration with asynchronous ReactPHP applications. + +* **Protocol Support:** Implements the `2024-11-05` version of the Model Context Protocol. + +* **Framework Agnostic:** Designed to work in vanilla PHP projects or integrated into any framework. + + +### 🚀 Getting Started + +Please refer to the [README.md](README.md) for detailed installation instructions, usage examples, and core concepts. Sample implementations for `stdio` and `reactphp` are available in the `samples/` directory. + +### ⚠️ Important Notes + +* When implementing the `http` transport using `HttpTransportHandler`, be aware of the critical server environment requirements detailed in the README regarding concurrent request handling for SSE. Standard synchronous PHP servers (like `php artisan serve` or basic Apache/Nginx setups) are generally **not suitable** without proper configuration for concurrency (e.g., PHP-FPM with multiple workers, Octane, Swoole, ReactPHP, RoadRunner, FrankenPHP). + +### Future Plans + +While this package focuses on the server implementation, future projects within the `php-mcp` organization may include client libraries and other utilities related to MCP in PHP. diff --git a/vendor/php-mcp/server/CONTRIBUTING.md b/vendor/php-mcp/server/CONTRIBUTING.md new file mode 100644 index 000000000..f913471e5 --- /dev/null +++ b/vendor/php-mcp/server/CONTRIBUTING.md @@ -0,0 +1,59 @@ +# Contributing to php-mcp/server + +First off, thank you for considering contributing to `php-mcp/server`! We appreciate your time and effort. This project aims to provide a robust and easy-to-use PHP server for the Model Context Protocol. + +Following these guidelines helps to communicate that you respect the time of the developers managing and developing this open-source project. In return, they should reciprocate that respect in addressing your issue, assessing changes, and helping you finalize your pull requests. + +## How Can I Contribute? + +There are several ways you can contribute: + +* **Reporting Bugs:** If you find a bug, please open an issue on the GitHub repository. Include steps to reproduce, expected behavior, and actual behavior. Specify your PHP version, operating system, and relevant package versions. +* **Suggesting Enhancements:** Open an issue to suggest new features or improvements to existing functionality. Explain the use case and why the enhancement would be valuable. +* **Improving Documentation:** If you find errors, omissions, or areas that could be clearer in the README or code comments, please submit a pull request or open an issue. +* **Writing Code:** Submit pull requests to fix bugs or add new features. + +## Development Setup + +1. **Fork the repository:** Click the "Fork" button on the [php-mcp/server GitHub page](https://github.com/php-mcp/server). +2. **Clone your fork:** `git clone git@github.com:YOUR_USERNAME/server.git` +3. **Navigate into the directory:** `cd server` +4. **Install dependencies:** `composer install` (This installs runtime and development dependencies). + +## Submitting Changes (Pull Requests) + +1. **Create a new branch:** `git checkout -b feature/your-feature-name` or `git checkout -b fix/issue-number`. +2. **Make your changes:** Write your code and accompanying tests. +3. **Ensure Code Style:** Run the code style fixer (if configured, e.g., PHP CS Fixer): + ```bash + composer lint # Or ./vendor/bin/php-cs-fixer fix + ``` + Adhere to PSR-12 coding standards. +4. **Run Tests:** Ensure all tests pass: + ```bash + composer test # Or ./vendor/bin/pest + ``` + Consider adding new tests for your changes. Aim for good test coverage. +5. **Update Documentation:** If your changes affect the public API or usage, update the `README.md` and relevant PHPDoc blocks. +6. **Commit your changes:** Use clear and descriptive commit messages. `git commit -m "feat: Add support for resource subscriptions"` or `git commit -m "fix: Correct handling of transport errors"` +7. **Push to your fork:** `git push origin feature/your-feature-name` +8. **Open a Pull Request:** Go to the original `php-mcp/server` repository on GitHub and open a pull request from your branch to the `main` branch (or the appropriate development branch). +9. **Describe your changes:** Provide a clear description of the problem and solution in the pull request. Link to any relevant issues (`Closes #123`). + +## Coding Standards + +* Follow **PSR-12** coding standards. +* Use **strict types:** `declare(strict_types=1);` at the top of PHP files. +* Use **PHP 8.1+ features** where appropriate (readonly properties, enums, etc.). +* Add **PHPDoc blocks** for all public classes, methods, and properties. +* Write clear and concise code. Add comments only where necessary to explain complex logic. + +## Reporting Issues + +* Use the GitHub issue tracker. +* Check if the issue already exists. +* Provide a clear title and description. +* Include steps to reproduce the issue, code examples, error messages, and stack traces if applicable. +* Specify relevant environment details (PHP version, OS, package version). + +Thank you for contributing! \ No newline at end of file diff --git a/vendor/php-mcp/server/LICENSE b/vendor/php-mcp/server/LICENSE new file mode 100644 index 000000000..8962e8420 --- /dev/null +++ b/vendor/php-mcp/server/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Kyrian Obikwelu + +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. \ No newline at end of file diff --git a/vendor/php-mcp/server/README.md b/vendor/php-mcp/server/README.md new file mode 100644 index 000000000..8707e6cd7 --- /dev/null +++ b/vendor/php-mcp/server/README.md @@ -0,0 +1,1436 @@ +# PHP MCP Server SDK + +[![Latest Version on Packagist](https://img.shields.io/packagist/v/php-mcp/server.svg?style=flat-square)](https://packagist.org/packages/php-mcp/server) +[![Total Downloads](https://img.shields.io/packagist/dt/php-mcp/server.svg?style=flat-square)](https://packagist.org/packages/php-mcp/server) +[![Tests](https://img.shields.io/github/actions/workflow/status/php-mcp/server/tests.yml?branch=main&style=flat-square)](https://github.com/php-mcp/server/actions/workflows/tests.yml) +[![License](https://img.shields.io/packagist/l/php-mcp/server.svg?style=flat-square)](LICENSE) + +**A comprehensive PHP SDK for building [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) servers. Create production-ready MCP servers in PHP with modern architecture, extensive testing, and flexible transport options.** + +This SDK enables you to expose your PHP application's functionality as standardized MCP **Tools**, **Resources**, and **Prompts**, allowing AI assistants (like Anthropic's Claude, Cursor IDE, OpenAI's ChatGPT, etc.) to interact with your backend using the MCP standard. + +## 🚀 Key Features + +- **🏗️ Modern Architecture**: Built with PHP 8.1+ features, PSR standards, and modular design +- **📡 Multiple Transports**: Supports `stdio`, `http+sse`, and new **streamable HTTP** with resumability +- **🎯 Attribute-Based Definition**: Use PHP 8 Attributes (`#[McpTool]`, `#[McpResource]`, etc.) for zero-config element registration +- **🔧 Flexible Handlers**: Support for closures, class methods, static methods, and invokable classes +- **📝 Smart Schema Generation**: Automatic JSON schema generation from method signatures with optional `#[Schema]` attribute enhancements +- **⚡ Session Management**: Advanced session handling with multiple storage backends +- **🔄 Event-Driven**: ReactPHP-based for high concurrency and non-blocking operations +- **📊 Batch Processing**: Full support for JSON-RPC batch requests +- **💾 Smart Caching**: Intelligent caching of discovered elements with manual override precedence +- **🧪 Completion Providers**: Built-in support for argument completion in tools and prompts +- **🔌 Dependency Injection**: Full PSR-11 container support with auto-wiring +- **📋 Comprehensive Testing**: Extensive test suite with integration tests for all transports + +This package supports the **2025-03-26** version of the Model Context Protocol with backward compatibility. + +## 📋 Requirements + +- **PHP** >= 8.1 +- **Composer** +- **For HTTP Transport**: An event-driven PHP environment (CLI recommended) +- **Extensions**: `json`, `mbstring`, `pcre` (typically enabled by default) + +## 📦 Installation + +```bash +composer require php-mcp/server +``` + +> **💡 Laravel Users**: Consider using [`php-mcp/laravel`](https://github.com/php-mcp/laravel) for enhanced framework integration, configuration management, and Artisan commands. + +## ⚡ Quick Start: Stdio Server with Discovery + +This example demonstrates the most common usage pattern - a `stdio` server using attribute discovery. + +**1. Define Your MCP Elements** + +Create `src/CalculatorElements.php`: + +```php +withServerInfo('PHP Calculator Server', '1.0.0') + ->build(); + + // Discover MCP elements via attributes + $server->discover( + basePath: __DIR__, + scanDirs: ['src'] + ); + + // Start listening via stdio transport + $transport = new StdioServerTransport(); + $server->listen($transport); + +} catch (\Throwable $e) { + fwrite(STDERR, "[CRITICAL ERROR] " . $e->getMessage() . "\n"); + exit(1); +} +``` + +**3. Configure Your MCP Client** + +Add to your client configuration (e.g., `.cursor/mcp.json`): + +```json +{ + "mcpServers": { + "php-calculator": { + "command": "php", + "args": ["/absolute/path/to/your/mcp-server.php"] + } + } +} +``` + +**4. Test the Server** + +Your AI assistant can now call: +- `add_numbers` - Add two integers +- `calculate_power` - Calculate power with validation constraints + +## 🏗️ Architecture Overview + +The PHP MCP Server uses a modern, decoupled architecture: + +``` +┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ +│ MCP Client │◄──►│ Transport │◄──►│ Protocol │ +│ (Claude, etc.) │ │ (Stdio/HTTP/SSE) │ │ (JSON-RPC) │ +└─────────────────┘ └──────────────────┘ └─────────────────┘ + │ + ┌─────────────────┐ │ + │ Session Manager │◄──────────────┤ + │ (Multi-backend) │ │ + └─────────────────┘ │ + │ +┌─────────────────┐ ┌──────────────────┐ │ +│ Dispatcher │◄───│ Server Core │◄─────────────┤ +│ (Method Router) │ │ Configuration │ │ +└─────────────────┘ └──────────────────┘ │ + │ │ + ▼ │ +┌─────────────────┐ ┌──────────────────┐ │ +│ Registry │ │ Elements │◄─────────────┘ +│ (Element Store)│◄──►│ (Tools/Resources │ +└─────────────────┘ │ Prompts/etc.) │ + └──────────────────┘ +``` + +### Core Components + +- **`ServerBuilder`**: Fluent configuration interface (`Server::make()->...->build()`) +- **`Server`**: Central coordinator containing all configured components +- **`Protocol`**: JSON-RPC 2.0 handler bridging transports and core logic +- **`SessionManager`**: Multi-backend session storage (array, cache, custom) +- **`Dispatcher`**: Method routing and request processing +- **`Registry`**: Element storage with smart caching and precedence rules +- **`Elements`**: Registered MCP components (Tools, Resources, Prompts, Templates) + +### Transport Options + +1. **`StdioServerTransport`**: Standard I/O for direct client launches +2. **`HttpServerTransport`**: HTTP + Server-Sent Events for web integration +3. **`StreamableHttpServerTransport`**: Enhanced HTTP with resumability and event sourcing + +## ⚙️ Server Configuration + +### Basic Configuration + +```php +use PhpMcp\Server\Server; +use PhpMcp\Schema\ServerCapabilities; + +$server = Server::make() + ->withServerInfo('My App Server', '2.1.0') + ->withCapabilities(ServerCapabilities::make( + resources: true, + resourcesSubscribe: true, + prompts: true, + tools: true + )) + ->withPaginationLimit(100) + ->build(); +``` + +### Advanced Configuration with Dependencies + +```php +use Psr\Log\Logger; +use Psr\SimpleCache\CacheInterface; +use Psr\Container\ContainerInterface; + +$server = Server::make() + ->withServerInfo('Production Server', '1.0.0') + ->withLogger($myPsrLogger) // PSR-3 Logger + ->withCache($myPsrCache) // PSR-16 Cache + ->withContainer($myPsrContainer) // PSR-11 Container + ->withSession('cache', 7200) // Cache-backed sessions, 2hr TTL + ->withPaginationLimit(50) // Limit list responses + ->build(); +``` + +### Session Management Options + +```php +// In-memory sessions (default, not persistent) +->withSession('array', 3600) + +// Cache-backed sessions (persistent across restarts) +->withSession('cache', 7200) + +// Custom session handler (implement SessionHandlerInterface) +->withSessionHandler(new MyCustomSessionHandler(), 1800) +``` + +## 🎯 Defining MCP Elements + +The server provides two powerful ways to define MCP elements: **Attribute-Based Discovery** (recommended) and **Manual Registration**. Both can be combined, with manual registrations taking precedence. + +### Element Types + +- **🔧 Tools**: Executable functions/actions (e.g., `calculate`, `send_email`, `query_database`) +- **📄 Resources**: Static content/data (e.g., `config://settings`, `file://readme.txt`) +- **📋 Resource Templates**: Dynamic resources with URI patterns (e.g., `user://{id}/profile`) +- **💬 Prompts**: Conversation starters/templates (e.g., `summarize`, `translate`) + +### 1. 🏷️ Attribute-Based Discovery (Recommended) + +Use PHP 8 attributes to mark methods or invokable classes as MCP elements. The server will discover them via filesystem scanning. + +```php +use PhpMcp\Server\Attributes\{McpTool, McpResource, McpResourceTemplate, McpPrompt}; + +class UserManager +{ + /** + * Creates a new user account. + */ + #[McpTool(name: 'create_user')] + public function createUser(string $email, string $password, string $role = 'user'): array + { + // Create user logic + return ['id' => 123, 'email' => $email, 'role' => $role]; + } + + /** + * Get user configuration. + */ + #[McpResource( + uri: 'config://user/settings', + mimeType: 'application/json' + )] + public function getUserConfig(): array + { + return ['theme' => 'dark', 'notifications' => true]; + } + + /** + * Get user profile by ID. + */ + #[McpResourceTemplate( + uriTemplate: 'user://{userId}/profile', + mimeType: 'application/json' + )] + public function getUserProfile(string $userId): array + { + return ['id' => $userId, 'name' => 'John Doe']; + } + + /** + * Generate welcome message prompt. + */ + #[McpPrompt(name: 'welcome_user')] + public function welcomeUserPrompt(string $username, string $role): array + { + return [ + ['role' => 'user', 'content' => "Create a welcome message for {$username} with role {$role}"] + ]; + } +} +``` + +**Discovery Process:** + +```php +// Build server first +$server = Server::make() + ->withServerInfo('My App Server', '1.0.0') + ->build(); + +// Then discover elements +$server->discover( + basePath: __DIR__, + scanDirs: ['src/Handlers', 'src/Services'], // Directories to scan + excludeDirs: ['src/Tests'], // Directories to skip + saveToCache: true // Cache results (default: true) +); +``` + +**Available Attributes:** + +- **`#[McpTool]`**: Executable actions +- **`#[McpResource]`**: Static content accessible via URI +- **`#[McpResourceTemplate]`**: Dynamic resources with URI templates +- **`#[McpPrompt]`**: Conversation templates and prompt generators + +### 2. 🔧 Manual Registration + +Register elements programmatically using the `ServerBuilder` before calling `build()`. Useful for dynamic registration, closures, or when you prefer explicit control. + +```php +use App\Handlers\{EmailHandler, ConfigHandler, UserHandler, PromptHandler}; +use PhpMcp\Schema\{ToolAnnotations, Annotations}; + +$server = Server::make() + ->withServerInfo('Manual Registration Server', '1.0.0') + + // Register a tool with handler method + ->withTool( + [EmailHandler::class, 'sendEmail'], // Handler: [class, method] + name: 'send_email', // Tool name (optional) + description: 'Send email to user', // Description (optional) + annotations: ToolAnnotations::make( // Annotations (optional) + title: 'Send Email Tool' + ) + ) + + // Register invokable class as tool + ->withTool(UserHandler::class) // Handler: Invokable class + + // Register a closure as tool + ->withTool( + function(int $a, int $b): int { // Handler: Closure + return $a + $b; + }, + name: 'add_numbers', + description: 'Add two numbers together' + ) + + // Register a resource with closure + ->withResource( + function(): array { // Handler: Closure + return ['timestamp' => time(), 'server' => 'php-mcp']; + }, + uri: 'config://runtime/status', // URI (required) + mimeType: 'application/json' // MIME type (optional) + ) + + // Register a resource template + ->withResourceTemplate( + [UserHandler::class, 'getUserProfile'], + uriTemplate: 'user://{userId}/profile' // URI template (required) + ) + + // Register a prompt with closure + ->withPrompt( + function(string $topic, string $tone = 'professional'): array { + return [ + ['role' => 'user', 'content' => "Write about {$topic} in a {$tone} tone"] + ]; + }, + name: 'writing_prompt' // Prompt name (optional) + ) + + ->build(); +``` + +The server supports three flexible handler formats: `[ClassName::class, 'methodName']` for class method handlers, `InvokableClass::class` for invokable class handlers (classes with `__invoke` method), and any PHP callable including closures, static methods like `[SomeClass::class, 'staticMethod']`, or function names. Class-based handlers are resolved via the configured PSR-11 container for dependency injection. Manual registrations are never cached and take precedence over discovered elements with the same identifier. + +> [!IMPORTANT] +> When using closures as handlers, the server generates minimal JSON schemas based only on PHP type hints since there are no docblocks or class context available. For more detailed schemas with validation constraints, descriptions, and formats, you have two options: +> +> - Use the [`#[Schema]` attribute](#-schema-generation-and-validation) for enhanced schema generation +> - Provide a custom `$inputSchema` parameter when registering tools with `->withTool()` + +### 🏆 Element Precedence & Discovery + +**Precedence Rules:** +- Manual registrations **always** override discovered/cached elements with the same identifier +- Discovered elements are cached for performance (configurable) +- Cache is automatically invalidated on fresh discovery runs + +**Discovery Process:** + +```php +$server->discover( + basePath: __DIR__, + scanDirs: ['src/Handlers', 'src/Services'], // Scan these directories + excludeDirs: ['tests', 'vendor'], // Skip these directories + force: false, // Force re-scan (default: false) + saveToCache: true // Save to cache (default: true) +); +``` + +**Caching Behavior:** +- Only **discovered** elements are cached (never manual registrations) +- Cache loaded automatically during `build()` if available +- Fresh `discover()` calls clear and rebuild cache +- Use `force: true` to bypass discovery-already-ran check + +## 🚀 Running the Server (Transports) + +The server core is transport-agnostic. Choose a transport based on your deployment needs: + +### 1. 📟 Stdio Transport + +**Best for**: Direct client execution, command-line tools, simple deployments + +```php +use PhpMcp\Server\Transports\StdioServerTransport; + +$server = Server::make() + ->withServerInfo('Stdio Server', '1.0.0') + ->build(); + +$server->discover(__DIR__, ['src']); + +// Create stdio transport (uses STDIN/STDOUT by default) +$transport = new StdioServerTransport(); + +// Start listening (blocking call) +$server->listen($transport); +``` + +**Client Configuration:** +```json +{ + "mcpServers": { + "my-php-server": { + "command": "php", + "args": ["/absolute/path/to/server.php"] + } + } +} +``` + +> ⚠️ **Important**: When using stdio transport, **never** write to `STDOUT` in your handlers (use `STDERR` for debugging). `STDOUT` is reserved for JSON-RPC communication. + +### 2. 🌐 HTTP + Server-Sent Events Transport (Deprecated) + +> ⚠️ **Note**: This transport is deprecated in the latest MCP protocol version but remains available for backwards compatibility. For new projects, use the [StreamableHttpServerTransport](#3--streamable-http-transport-new) which provides enhanced features and better protocol compliance. + +**Best for**: Legacy applications requiring backwards compatibility + +```php +use PhpMcp\Server\Transports\HttpServerTransport; + +$server = Server::make() + ->withServerInfo('HTTP Server', '1.0.0') + ->withLogger($logger) // Recommended for HTTP + ->build(); + +$server->discover(__DIR__, ['src']); + +// Create HTTP transport +$transport = new HttpServerTransport( + host: '127.0.0.1', // MCP protocol prohibits 0.0.0.0 + port: 8080, // Port number + mcpPathPrefix: 'mcp' // URL prefix (/mcp/sse, /mcp/message) +); + +$server->listen($transport); +``` + +**Client Configuration:** +```json +{ + "mcpServers": { + "my-http-server": { + "url": "http://localhost:8080/mcp/sse" + } + } +} +``` + +**Endpoints:** +- **SSE Connection**: `GET /mcp/sse` +- **Message Sending**: `POST /mcp/message?clientId={clientId}` + +### 3. 🔄 Streamable HTTP Transport (Recommended) + +**Best for**: Production deployments, remote MCP servers, multiple clients, resumable connections + +```php +use PhpMcp\Server\Transports\StreamableHttpServerTransport; + +$server = Server::make() + ->withServerInfo('Streamable Server', '1.0.0') + ->withLogger($logger) + ->withCache($cache) // Required for resumability + ->build(); + +$server->discover(__DIR__, ['src']); + +// Create streamable transport with resumability +$transport = new StreamableHttpServerTransport( + host: '127.0.0.1', // MCP protocol prohibits 0.0.0.0 + port: 8080, + mcpPathPrefix: 'mcp', + enableJsonResponse: false, // Use SSE streaming (default) + stateless: false // Enable stateless mode for session-less clients +); + +$server->listen($transport); +``` + +**JSON Response Mode:** + +The `enableJsonResponse` option controls how responses are delivered: + +- **`false` (default)**: Uses Server-Sent Events (SSE) streams for responses. Best for tools that may take time to process. +- **`true`**: Returns immediate JSON responses without opening SSE streams. Use this when your tools execute quickly and don't need streaming. + +```php +// For fast-executing tools, enable JSON mode +$transport = new StreamableHttpServerTransport( + host: '127.0.0.1', + port: 8080, + enableJsonResponse: true // Immediate JSON responses +); +``` + +**Stateless Mode:** + +For clients that have issues with session management, enable stateless mode: + +```php +$transport = new StreamableHttpServerTransport( + host: '127.0.0.1', + port: 8080, + stateless: true // Each request is independent +); +``` + +In stateless mode, session IDs are generated internally but not exposed to clients, and each request is treated as independent without persistent session state. + +**Features:** +- **Resumable connections** - clients can reconnect and replay missed events +- **Event sourcing** - all events are stored for replay +- **JSON mode** - optional JSON-only responses for fast tools +- **Enhanced session management** - persistent session state +- **Multiple client support** - designed for concurrent clients +- **Stateless mode** - session-less operation for simple clients + +## 📋 Schema Generation and Validation + +The server automatically generates JSON schemas for tool parameters using a sophisticated priority system that combines PHP type hints, docblock information, and the optional `#[Schema]` attribute. These generated schemas are used both for input validation and for providing schema information to MCP clients. + +### Schema Generation Priority + +The server follows this order of precedence when generating schemas: + +1. **`#[Schema]` attribute with `definition`** - Complete schema override (highest precedence) +2. **Parameter-level `#[Schema]` attribute** - Parameter-specific schema enhancements +3. **Method-level `#[Schema]` attribute** - Method-wide schema configuration +4. **PHP type hints + docblocks** - Automatic inference from code (lowest precedence) + +When a `definition` is provided in the Schema attribute, all automatic inference is bypassed and the complete definition is used as-is. + +### Parameter-Level Schema Attributes + +```php +use PhpMcp\Server\Attributes\{McpTool, Schema}; + +#[McpTool(name: 'validate_user')] +public function validateUser( + #[Schema(format: 'email')] // PHP already knows it's string + string $email, + + #[Schema( + pattern: '^[A-Z][a-z]+$', + description: 'Capitalized name' + )] + string $name, + + #[Schema(minimum: 18, maximum: 120)] // PHP already knows it's integer + int $age +): bool { + return filter_var($email, FILTER_VALIDATE_EMAIL) !== false; +} +``` + +### Method-Level Schema + +```php +/** + * Process user data with nested validation. + */ +#[McpTool(name: 'create_user')] +#[Schema( + properties: [ + 'profile' => [ + 'type' => 'object', + 'properties' => [ + 'name' => ['type' => 'string', 'minLength' => 2], + 'age' => ['type' => 'integer', 'minimum' => 18], + 'email' => ['type' => 'string', 'format' => 'email'] + ], + 'required' => ['name', 'email'] + ] + ], + required: ['profile'] +)] +public function createUser(array $userData): array +{ + // PHP type hint provides base 'array' type + // Method-level Schema adds object structure validation + return ['id' => 123, 'status' => 'created']; +} +``` + +### Complete Schema Override (Method-Level Only) + +```php +#[McpTool(name: 'process_api_request')] +#[Schema(definition: [ + 'type' => 'object', + 'properties' => [ + 'endpoint' => ['type' => 'string', 'format' => 'uri'], + 'method' => ['type' => 'string', 'enum' => ['GET', 'POST', 'PUT', 'DELETE']], + 'headers' => [ + 'type' => 'object', + 'patternProperties' => [ + '^[A-Za-z0-9-]+$' => ['type' => 'string'] + ] + ] + ], + 'required' => ['endpoint', 'method'] +])] +public function processApiRequest(string $endpoint, string $method, array $headers): array +{ + // PHP type hints are completely ignored when definition is provided + // The schema definition above takes full precedence + return ['status' => 'processed', 'endpoint' => $endpoint]; +} +``` + +> ⚠️ **Important**: Complete schema definition override should rarely be used. It bypasses all automatic schema inference and requires you to define the entire JSON schema manually. Only use this if you're well-versed with JSON Schema specification and have complex validation requirements that cannot be achieved through the priority system. In most cases, parameter-level and method-level `#[Schema]` attributes provide sufficient flexibility. + +## 🎨 Return Value Formatting + +The server automatically formats return values from your handlers into appropriate MCP content types: + +### Automatic Formatting + +```php +// Simple values are auto-wrapped in TextContent +public function getString(): string { return "Hello World"; } // → TextContent +public function getNumber(): int { return 42; } // → TextContent +public function getBool(): bool { return true; } // → TextContent +public function getArray(): array { return ['key' => 'value']; } // → TextContent (JSON) + +// Null handling +public function getNull(): ?string { return null; } // → TextContent("(null)") +public function returnVoid(): void { /* no return */ } // → Empty content +``` + +### Advanced Content Types + +```php +use PhpMcp\Schema\Content\{TextContent, ImageContent, AudioContent, ResourceContent}; + +public function getFormattedCode(): TextContent +{ + return TextContent::code(' 'File content here', + 'mimeType' => 'text/plain' + ]; + + // Or for binary data: + // return [ + // 'blob' => base64_encode($binaryData), + // 'mimeType' => 'application/octet-stream' + // ]; +} +``` + +## 🔄 Batch Processing + +The server automatically handles JSON-RPC batch requests: + +```php +// Client can send multiple requests in a single HTTP call: +[ + {"jsonrpc": "2.0", "id": "1", "method": "tools/call", "params": {...}}, + {"jsonrpc": "2.0", "method": "notifications/ping"}, // notification + {"jsonrpc": "2.0", "id": "2", "method": "tools/call", "params": {...}} +] + +// Server returns batch response (excluding notifications): +[ + {"jsonrpc": "2.0", "id": "1", "result": {...}}, + {"jsonrpc": "2.0", "id": "2", "result": {...}} +] +``` + +## 🔧 Advanced Features + +### Completion Providers + +Completion providers enable MCP clients to offer auto-completion suggestions in their user interfaces. They are specifically designed for **Resource Templates** and **Prompts** to help users discover available options for dynamic parts like template variables or prompt arguments. + +> **Note**: Tools and resources can be discovered via standard MCP commands (`tools/list`, `resources/list`), so completion providers are not needed for them. Completion providers are used only for resource templates (URI variables) and prompt arguments. + +The `#[CompletionProvider]` attribute supports three types of completion sources: + +#### 1. Custom Provider Classes + +For complex completion logic, implement the `CompletionProviderInterface`: + +```php +use PhpMcp\Server\Contracts\CompletionProviderInterface; +use PhpMcp\Server\Contracts\SessionInterface; +use PhpMcp\Server\Attributes\{McpResourceTemplate, CompletionProvider}; + +class UserIdCompletionProvider implements CompletionProviderInterface +{ + public function __construct(private DatabaseService $db) {} + + public function getCompletions(string $currentValue, SessionInterface $session): array + { + // Dynamic completion from database + return $this->db->searchUsers($currentValue); + } +} + +class UserService +{ + #[McpResourceTemplate(uriTemplate: 'user://{userId}/profile')] + public function getUserProfile( + #[CompletionProvider(provider: UserIdCompletionProvider::class)] // Class string - resolved from container + string $userId + ): array { + return ['id' => $userId, 'name' => 'John Doe']; + } +} +``` + +You can also pass pre-configured provider instances: + +```php +class DocumentService +{ + #[McpPrompt(name: 'document_prompt')] + public function generatePrompt( + #[CompletionProvider(provider: new UserIdCompletionProvider($database))] // Pre-configured instance + string $userId, + + #[CompletionProvider(provider: $this->categoryProvider)] // Instance from property + string $category + ): array { + return [['role' => 'user', 'content' => "Generate document for user {$userId} in {$category}"]]; + } +} +``` + +#### 2. Simple List Completions + +For static completion lists, use the `values` parameter: + +```php +use PhpMcp\Server\Attributes\{McpPrompt, CompletionProvider}; + +class ContentService +{ + #[McpPrompt(name: 'content_generator')] + public function generateContent( + #[CompletionProvider(values: ['blog', 'article', 'tutorial', 'guide', 'documentation'])] + string $contentType, + + #[CompletionProvider(values: ['beginner', 'intermediate', 'advanced', 'expert'])] + string $difficulty + ): array { + return [['role' => 'user', 'content' => "Create a {$difficulty} level {$contentType}"]]; + } +} +``` + +#### 3. Enum-Based Completions + +For enum classes, use the `enum` parameter: + +```php +enum Priority: string +{ + case LOW = 'low'; + case MEDIUM = 'medium'; + case HIGH = 'high'; + case CRITICAL = 'critical'; +} + +enum Status // Unit enum (no backing values) +{ + case DRAFT; + case PUBLISHED; + case ARCHIVED; +} + +class TaskService +{ + #[McpTool(name: 'create_task')] + public function createTask( + string $title, + + #[CompletionProvider(enum: Priority::class)] // String-backed enum uses values + string $priority, + + #[CompletionProvider(enum: Status::class)] // Unit enum uses case names + string $status + ): array { + return ['id' => 123, 'title' => $title, 'priority' => $priority, 'status' => $status]; + } +} +``` + +#### Manual Registration with Completion Providers + +```php +$server = Server::make() + ->withServerInfo('Completion Demo', '1.0.0') + + // Using provider class (resolved from container) + ->withPrompt( + [DocumentHandler::class, 'generateReport'], + name: 'document_report' + // Completion providers are auto-discovered from method attributes + ) + + // Using closure with inline completion providers + ->withPrompt( + function( + #[CompletionProvider(values: ['json', 'xml', 'csv', 'yaml'])] + string $format, + + #[CompletionProvider(enum: Priority::class)] + string $priority + ): array { + return [['role' => 'user', 'content' => "Export data in {$format} format with {$priority} priority"]]; + }, + name: 'export_data' + ) + + ->build(); +``` + +#### Completion Provider Resolution + +The server automatically handles provider resolution: + +- **Class strings** (`MyProvider::class`) → Resolved from PSR-11 container with dependency injection +- **Instances** (`new MyProvider()`) → Used directly as-is +- **Values arrays** (`['a', 'b', 'c']`) → Automatically wrapped in `ListCompletionProvider` +- **Enum classes** (`MyEnum::class`) → Automatically wrapped in `EnumCompletionProvider` + +> **Important**: Completion providers only offer suggestions to users in the MCP client interface. Users can still input any value, so always validate parameters in your handlers regardless of completion provider constraints. + +### Custom Dependency Injection + +Your MCP element handlers can use constructor dependency injection to access services like databases, APIs, or other business logic. When handlers have constructor dependencies, you must provide a pre-configured PSR-11 container that contains those dependencies. + +By default, the server uses a `BasicContainer` - a simple implementation that attempts to auto-wire dependencies by instantiating classes with parameterless constructors. For dependencies that require configuration (like database connections), you can either manually add them to the BasicContainer or use a more advanced PSR-11 container like PHP-DI or Laravel's container. + +```php +use Psr\Container\ContainerInterface; + +class DatabaseService +{ + public function __construct(private \PDO $pdo) {} + + #[McpTool(name: 'query_users')] + public function queryUsers(): array + { + $stmt = $this->pdo->query('SELECT * FROM users'); + return $stmt->fetchAll(); + } +} + +// Option 1: Use the basic container and manually add dependencies +$basicContainer = new \PhpMcp\Server\Defaults\BasicContainer(); +$basicContainer->set(\PDO::class, new \PDO('sqlite::memory:')); + +// Option 2: Use any PSR-11 compatible container (PHP-DI, Laravel, etc.) +$container = new \DI\Container(); +$container->set(\PDO::class, new \PDO('mysql:host=localhost;dbname=app', $user, $pass)); + +$server = Server::make() + ->withContainer($basicContainer) // Handlers get dependencies auto-injected + ->build(); +``` + +### Resource Subscriptions + +```php +use PhpMcp\Schema\ServerCapabilities; + +$server = Server::make() + ->withCapabilities(ServerCapabilities::make( + resourcesSubscribe: true, // Enable resource subscriptions + prompts: true, + tools: true + )) + ->build(); + +// In your resource handler, you can notify clients of changes: +#[McpResource(uri: 'file://config.json')] +public function getConfig(): array +{ + // When config changes, notify subscribers + $this->notifyResourceChange('file://config.json'); + return ['setting' => 'value']; +} +``` + +### Resumability and Event Store + +For production deployments using `StreamableHttpServerTransport`, you can implement resumability with event sourcing by providing a custom event store: + +```php +use PhpMcp\Server\Contracts\EventStoreInterface; +use PhpMcp\Server\Defaults\InMemoryEventStore; +use PhpMcp\Server\Transports\StreamableHttpServerTransport; + +// Use the built-in in-memory event store (for development/testing) +$eventStore = new InMemoryEventStore(); + +// Or implement your own persistent event store +class DatabaseEventStore implements EventStoreInterface +{ + public function storeEvent(string $streamId, string $message): string + { + // Store event in database and return unique event ID + return $this->database->insert('events', [ + 'stream_id' => $streamId, + 'message' => $message, + 'created_at' => now() + ]); + } + + public function replayEventsAfter(string $lastEventId, callable $sendCallback): void + { + // Replay events for resumability + $events = $this->database->getEventsAfter($lastEventId); + foreach ($events as $event) { + $sendCallback($event['id'], $event['message']); + } + } +} + +// Configure transport with event store +$transport = new StreamableHttpServerTransport( + host: '127.0.0.1', + port: 8080, + eventStore: new DatabaseEventStore() // Enable resumability +); +``` + +### Custom Session Handlers + +Implement custom session storage by creating a class that implements `SessionHandlerInterface`: + +```php +use PhpMcp\Server\Contracts\SessionHandlerInterface; + +class DatabaseSessionHandler implements SessionHandlerInterface +{ + public function __construct(private \PDO $db) {} + + public function read(string $id): string|false + { + $stmt = $this->db->prepare('SELECT data FROM sessions WHERE id = ?'); + $stmt->execute([$id]); + $session = $stmt->fetch(\PDO::FETCH_ASSOC); + return $session ? $session['data'] : false; + } + + public function write(string $id, string $data): bool + { + $stmt = $this->db->prepare( + 'INSERT OR REPLACE INTO sessions (id, data, updated_at) VALUES (?, ?, ?)' + ); + return $stmt->execute([$id, $data, time()]); + } + + public function destroy(string $id): bool + { + $stmt = $this->db->prepare('DELETE FROM sessions WHERE id = ?'); + return $stmt->execute([$id]); + } + + public function gc(int $maxLifetime): array + { + $cutoff = time() - $maxLifetime; + $stmt = $this->db->prepare('DELETE FROM sessions WHERE updated_at < ?'); + $stmt->execute([$cutoff]); + return []; // Return array of cleaned session IDs if needed + } +} + +// Use custom session handler +$server = Server::make() + ->withSessionHandler(new DatabaseSessionHandler(), 3600) + ->build(); +``` + +### SSL Context Configuration + +For HTTPS deployments of `StreamableHttpServerTransport`, configure SSL context options: + +```php +$sslContext = [ + 'ssl' => [ + 'local_cert' => '/path/to/certificate.pem', + 'local_pk' => '/path/to/private-key.pem', + 'verify_peer' => false, + 'allow_self_signed' => true, + ] +]; + +$transport = new StreamableHttpServerTransport( + host: '0.0.0.0', + port: 8443, + sslContext: $sslContext +); +``` + +> **SSL Context Reference**: For complete SSL context options, see the [PHP SSL Context Options documentation](https://www.php.net/manual/en/context.ssl.php). +## 🔍 Error Handling & Debugging + +The server provides comprehensive error handling and debugging capabilities: + +### Exception Handling + +Tool handlers can throw any PHP exception when errors occur. The server automatically converts these exceptions into proper JSON-RPC error responses for MCP clients. + +```php +#[McpTool(name: 'divide_numbers')] +public function divideNumbers(float $dividend, float $divisor): float +{ + if ($divisor === 0.0) { + // Any exception with descriptive message will be sent to client + throw new \InvalidArgumentException('Division by zero is not allowed'); + } + + return $dividend / $divisor; +} + +#[McpTool(name: 'calculate_factorial')] +public function calculateFactorial(int $number): int +{ + if ($number < 0) { + throw new \InvalidArgumentException('Factorial is not defined for negative numbers'); + } + + if ($number > 20) { + throw new \OverflowException('Number too large, factorial would cause overflow'); + } + + // Implementation continues... + return $this->factorial($number); +} +``` + +The server will convert these exceptions into appropriate JSON-RPC error responses that MCP clients can understand and display to users. + +### Logging and Debugging + +```php +use Psr\Log\LoggerInterface; + +class DebugAwareHandler +{ + public function __construct(private LoggerInterface $logger) {} + + #[McpTool(name: 'debug_tool')] + public function debugTool(string $data): array + { + $this->logger->info('Processing debug tool', ['input' => $data]); + + // For stdio transport, use STDERR for debug output + fwrite(STDERR, "Debug: Processing data length: " . strlen($data) . "\n"); + + return ['processed' => true]; + } +} +``` + +## 🚀 Production Deployment + +Since `$server->listen()` runs a persistent process, you can deploy it using any strategy that suits your infrastructure needs. The server can be deployed on VPS, cloud instances, containers, or any environment that supports long-running processes. + +Here are two popular deployment approaches to consider: + +### Option 1: VPS with Supervisor + Nginx (Recommended) + +**Best for**: Most production deployments, cost-effective, full control + +```bash +# 1. Install your application on VPS +git clone https://github.com/yourorg/your-mcp-server.git /var/www/mcp-server +cd /var/www/mcp-server +composer install --no-dev --optimize-autoloader + +# 2. Install Supervisor +sudo apt-get install supervisor + +# 3. Create Supervisor configuration +sudo nano /etc/supervisor/conf.d/mcp-server.conf +``` + +**Supervisor Configuration:** +```ini +[program:mcp-server] +process_name=%(program_name)s_%(process_num)02d +command=php /var/www/mcp-server/server.php --transport=http --host=127.0.0.1 --port=8080 +autostart=true +autorestart=true +stopasgroup=true +killasgroup=true +user=www-data +numprocs=1 +redirect_stderr=true +stdout_logfile=/var/log/mcp-server.log +stdout_logfile_maxbytes=10MB +stdout_logfile_backups=3 +``` + +**Nginx Configuration with SSL:** +```nginx +# /etc/nginx/sites-available/mcp-server +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name mcp.yourdomain.com; + + # SSL configuration + ssl_certificate /etc/letsencrypt/live/mcp.yourdomain.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/mcp.yourdomain.com/privkey.pem; + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + + # MCP Server proxy + location / { + proxy_http_version 1.1; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + # Important for SSE connections + proxy_buffering off; + proxy_cache off; + + proxy_pass http://127.0.0.1:8080/; + } +} + +# Redirect HTTP to HTTPS +server { + listen 80; + listen [::]:80; + server_name mcp.yourdomain.com; + return 301 https://$server_name$request_uri; +} +``` + +**Start Services:** +```bash +# Enable and start supervisor +sudo supervisorctl reread +sudo supervisorctl update +sudo supervisorctl start mcp-server:* + +# Enable and start nginx +sudo systemctl enable nginx +sudo systemctl restart nginx + +# Check status +sudo supervisorctl status +``` + +**Client Configuration:** +```json +{ + "mcpServers": { + "my-server": { + "url": "https://mcp.yourdomain.com/mcp" + } + } +} +``` + +### Option 2: Docker Deployment + +**Best for**: Containerized environments, Kubernetes, cloud platforms + +**Production Dockerfile:** +```dockerfile +FROM php:8.3-fpm-alpine + +# Install system dependencies +RUN apk --no-cache add \ + nginx \ + supervisor \ + && docker-php-ext-enable opcache + +# Install PHP extensions for MCP +RUN docker-php-ext-install pdo_mysql pdo_sqlite opcache + +# Create application directory +WORKDIR /var/www/mcp + +# Copy application code +COPY . /var/www/mcp +COPY docker/nginx.conf /etc/nginx/nginx.conf +COPY docker/supervisord.conf /etc/supervisord.conf +COPY docker/php.ini /usr/local/etc/php/conf.d/production.ini + +# Install Composer dependencies +RUN composer install --no-dev --optimize-autoloader --no-interaction + +# Set permissions +RUN chown -R www-data:www-data /var/www/mcp + +# Expose port +EXPOSE 80 + +# Start supervisor +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"] +``` + +**docker-compose.yml:** +```yaml +services: + mcp-server: + build: . + ports: + - "8080:80" + environment: + - MCP_ENV=production + - MCP_LOG_LEVEL=info + volumes: + - ./storage:/var/www/mcp/storage + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost/health"] + interval: 30s + timeout: 10s + retries: 3 + + # Optional: Add database if needed + database: + image: mysql:8.0 + environment: + MYSQL_ROOT_PASSWORD: secure_password + MYSQL_DATABASE: mcp_server + volumes: + - mysql_data:/var/lib/mysql + restart: unless-stopped + +volumes: + mysql_data: +``` + +### Security Best Practices + +1. **Firewall Configuration:** +```bash +# Only allow necessary ports +sudo ufw allow ssh +sudo ufw allow 80 +sudo ufw allow 443 +sudo ufw deny 8080 # MCP port should not be publicly accessible +sudo ufw enable +``` + +2. **SSL/TLS Setup:** +```bash +# Install Certbot for Let's Encrypt +sudo apt install certbot python3-certbot-nginx + +# Generate SSL certificate +sudo certbot --nginx -d mcp.yourdomain.com +``` + +## 📚 Examples & Use Cases + +Explore comprehensive examples in the [`examples/`](./examples/) directory: + +### Available Examples + +- **`01-discovery-stdio-calculator/`** - Basic stdio calculator with attribute discovery +- **`02-discovery-http-userprofile/`** - HTTP server with user profile management +- **`03-manual-registration-stdio/`** - Manual element registration patterns +- **`04-combined-registration-http/`** - Combining manual and discovered elements +- **`05-stdio-env-variables/`** - Environment variable handling +- **`06-custom-dependencies-stdio/`** - Dependency injection with task management +- **`07-complex-tool-schema-http/`** - Advanced schema validation examples +- **`08-schema-showcase-streamable/`** - Comprehensive schema feature showcase + +### Running Examples + +```bash +# Navigate to an example directory +cd examples/01-discovery-stdio-calculator/ + +# Make the server executable +chmod +x server.php + +# Run the server (or configure it in your MCP client) +./server.php +``` + +## 🚧 Migration from v2.x + +If migrating from version 2.x, note these key changes: + +### Schema Updates +- Uses `php-mcp/schema` package for DTOs instead of internal classes +- Content types moved to `PhpMcp\Schema\Content\*` namespace +- Updated method signatures for better type safety + +### Session Management +- New session management with multiple backends +- Use `->withSession()` or `->withSessionHandler()` for configuration +- Sessions are now persistent across reconnections (with cache backend) + +### Transport Changes +- New `StreamableHttpServerTransport` with resumability +- Enhanced error handling and event sourcing +- Better batch request processing + +## 🧪 Testing + +```bash +# Install development dependencies +composer install --dev + +# Run the test suite +composer test + +# Run tests with coverage (requires Xdebug) +composer test:coverage + +# Run code style checks +composer lint +``` + +## 🤝 Contributing + +We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. + +## 📄 License + +The MIT License (MIT). See [LICENSE](LICENSE) for details. + +## 🙏 Acknowledgments + +- Built on the [Model Context Protocol](https://modelcontextprotocol.io/) specification +- Powered by [ReactPHP](https://reactphp.org/) for async operations +- Uses [PSR standards](https://www.php-fig.org/) for maximum interoperability diff --git a/vendor/php-mcp/server/composer.json b/vendor/php-mcp/server/composer.json new file mode 100644 index 000000000..aed46e4e5 --- /dev/null +++ b/vendor/php-mcp/server/composer.json @@ -0,0 +1,74 @@ +{ + "name": "php-mcp/server", + "description": "PHP SDK for building Model Context Protocol (MCP) servers - Create MCP tools, resources, and prompts", + "keywords": [ + "mcp", + "model context protocol", + "server", + "php", + "php mcp", + "php mcp sdk", + "php mcp server", + "php mcp tools", + "php mcp resources", + "php mcp prompts", + "php model context protocol" + ], + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Kyrian Obikwelu", + "email": "koshnawaza@gmail.com" + } + ], + "require": { + "php": ">=8.1", + "opis/json-schema": "^2.4", + "php-mcp/schema": "^1.0", + "phpdocumentor/reflection-docblock": "^5.6", + "psr/clock": "^1.0", + "psr/container": "^1.0 || ^2.0", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "psr/simple-cache": "^1.0 || ^2.0 || ^3.0", + "react/event-loop": "^1.5", + "react/http": "^1.11", + "react/promise": "^3.0", + "react/stream": "^1.4", + "symfony/finder": "^6.4 || ^7.2" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.75", + "mockery/mockery": "^1.6", + "pestphp/pest": "^2.36.0|^3.5.0", + "react/async": "^4.0", + "react/child-process": "^0.6.6", + "symfony/var-dumper": "^6.4.11|^7.1.5" + }, + "suggest": { + "ext-pcntl": "For signal handling support when using StdioServerTransport with StreamSelectLoop" + }, + "autoload": { + "psr-4": { + "PhpMcp\\Server\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "PhpMcp\\Server\\Tests\\": "tests/" + } + }, + "scripts": { + "test": "vendor/bin/pest", + "test:coverage": "XDEBUG_MODE=coverage ./vendor/bin/pest --coverage", + "lint": "vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php" + }, + "config": { + "sort-packages": true, + "allow-plugins": { + "pestphp/pest-plugin": true + } + }, + "minimum-stability": "dev", + "prefer-stable": true +} \ No newline at end of file diff --git a/vendor/php-mcp/server/examples/01-discovery-stdio-calculator/McpElements.php b/vendor/php-mcp/server/examples/01-discovery-stdio-calculator/McpElements.php new file mode 100644 index 000000000..6b710100e --- /dev/null +++ b/vendor/php-mcp/server/examples/01-discovery-stdio-calculator/McpElements.php @@ -0,0 +1,127 @@ + 2, + 'allow_negative' => true, + ]; + + /** + * Performs a calculation based on the operation. + * + * Supports 'add', 'subtract', 'multiply', 'divide'. + * Obeys the 'precision' and 'allow_negative' settings from the config resource. + * + * @param float $a The first operand. + * @param float $b The second operand. + * @param string $operation The operation ('add', 'subtract', 'multiply', 'divide'). + * @return float|string The result of the calculation, or an error message string. + */ + #[McpTool(name: 'calculate')] + public function calculate(float $a, float $b, string $operation): float|string + { + // Use STDERR for logs + fwrite(STDERR, "Calculate tool called: a=$a, b=$b, op=$operation\n"); + + $op = strtolower($operation); + $result = null; + + switch ($op) { + case 'add': + $result = $a + $b; + break; + case 'subtract': + $result = $a - $b; + break; + case 'multiply': + $result = $a * $b; + break; + case 'divide': + if ($b == 0) { + return 'Error: Division by zero.'; + } + $result = $a / $b; + break; + default: + return "Error: Unknown operation '{$operation}'. Supported: add, subtract, multiply, divide."; + } + + if (! $this->config['allow_negative'] && $result < 0) { + return 'Error: Negative results are disabled.'; + } + + return round($result, $this->config['precision']); + } + + /** + * Provides the current calculator configuration. + * Can be read by clients to understand precision etc. + * + * @return array The configuration array. + */ + #[McpResource( + uri: 'config://calculator/settings', + name: 'calculator_config', + description: 'Current settings for the calculator tool (precision, allow_negative).', + mimeType: 'application/json' // Return as JSON + )] + public function getConfiguration(): array + { + fwrite(STDERR, "Resource config://calculator/settings read.\n"); + + return $this->config; + } + + /** + * Updates a specific configuration setting. + * Note: This requires more robust validation in a real app. + * + * @param string $setting The setting key ('precision' or 'allow_negative'). + * @param mixed $value The new value (int for precision, bool for allow_negative). + * @return array Success message or error. + */ + #[McpTool(name: 'update_setting')] + public function updateSetting(string $setting, mixed $value): array + { + fwrite(STDERR, "Update Setting tool called: setting=$setting, value=".var_export($value, true)."\n"); + if (! array_key_exists($setting, $this->config)) { + return ['success' => false, 'error' => "Unknown setting '{$setting}'."]; + } + + if ($setting === 'precision') { + if (! is_int($value) || $value < 0 || $value > 10) { + return ['success' => false, 'error' => 'Invalid precision value. Must be integer between 0 and 10.']; + } + $this->config['precision'] = $value; + + // In real app, notify subscribers of config://calculator/settings change + // $registry->notifyResourceChanged('config://calculator/settings'); + return ['success' => true, 'message' => "Precision updated to {$value}."]; + } + + if ($setting === 'allow_negative') { + if (! is_bool($value)) { + // Attempt basic cast for flexibility + if (in_array(strtolower((string) $value), ['true', '1', 'yes', 'on'])) { + $value = true; + } elseif (in_array(strtolower((string) $value), ['false', '0', 'no', 'off'])) { + $value = false; + } else { + return ['success' => false, 'error' => 'Invalid allow_negative value. Must be boolean (true/false).']; + } + } + $this->config['allow_negative'] = $value; + + // $registry->notifyResourceChanged('config://calculator/settings'); + return ['success' => true, 'message' => 'Allow negative results set to '.($value ? 'true' : 'false').'.']; + } + + return ['success' => false, 'error' => 'Internal error handling setting.']; // Should not happen + } +} diff --git a/vendor/php-mcp/server/examples/01-discovery-stdio-calculator/server.php b/vendor/php-mcp/server/examples/01-discovery-stdio-calculator/server.php new file mode 100644 index 000000000..1895a37fc --- /dev/null +++ b/vendor/php-mcp/server/examples/01-discovery-stdio-calculator/server.php @@ -0,0 +1,82 @@ +#!/usr/bin/env php +discover() + | scans the current directory (specified by basePath: __DIR__, scanDirs: ['.']) + | to find and register elements before listening on STDIN/STDOUT. + | + | If you provided a `CacheInterface` implementation to the ServerBuilder, + | the discovery process will be cached, so you can comment out the + | discovery call after the first run to speed up subsequent runs. + | +*/ +declare(strict_types=1); + +chdir(__DIR__); +require_once '../../vendor/autoload.php'; +require_once 'McpElements.php'; + +use PhpMcp\Server\Server; +use PhpMcp\Server\Transports\StdioServerTransport; +use Psr\Log\AbstractLogger; + +class StderrLogger extends AbstractLogger +{ + public function log($level, \Stringable|string $message, array $context = []): void + { + fwrite(STDERR, sprintf( + "[%s] %s %s\n", + strtoupper($level), + $message, + empty($context) ? '' : json_encode($context) + )); + } +} + +try { + $logger = new StderrLogger(); + $logger->info('Starting MCP Stdio Calculator Server...'); + + $server = Server::make() + ->withServerInfo('Stdio Calculator', '1.1.0') + ->withLogger($logger) + ->build(); + + $server->discover(__DIR__, ['.']); + + $transport = new StdioServerTransport(); + + $server->listen($transport); + + $logger->info('Server listener stopped gracefully.'); + exit(0); + +} catch (\Throwable $e) { + fwrite(STDERR, "[MCP SERVER CRITICAL ERROR]\n"); + fwrite(STDERR, 'Error: '.$e->getMessage()."\n"); + fwrite(STDERR, 'File: '.$e->getFile().':'.$e->getLine()."\n"); + fwrite(STDERR, $e->getTraceAsString()."\n"); + exit(1); +} diff --git a/vendor/php-mcp/server/examples/02-discovery-http-userprofile/McpElements.php b/vendor/php-mcp/server/examples/02-discovery-http-userprofile/McpElements.php new file mode 100644 index 000000000..f684814e8 --- /dev/null +++ b/vendor/php-mcp/server/examples/02-discovery-http-userprofile/McpElements.php @@ -0,0 +1,133 @@ + ['name' => 'Alice', 'email' => 'alice@example.com', 'role' => 'admin'], + '102' => ['name' => 'Bob', 'email' => 'bob@example.com', 'role' => 'user'], + '103' => ['name' => 'Charlie', 'email' => 'charlie@example.com', 'role' => 'user'], + ]; + + private LoggerInterface $logger; + + public function __construct(LoggerInterface $logger) + { + $this->logger = $logger; + $this->logger->debug('HttpUserProfileExample McpElements instantiated.'); + } + + /** + * Retrieves the profile data for a specific user. + * + * @param string $userId The ID of the user (from URI). + * @return array User profile data. + * + * @throws McpServerException If the user is not found. + */ + #[McpResourceTemplate( + uriTemplate: 'user://{userId}/profile', + name: 'user_profile', + description: 'Get profile information for a specific user ID.', + mimeType: 'application/json' + )] + + public function getUserProfile( + #[CompletionProvider(values: ['101', '102', '103'])] + string $userId + ): array { + $this->logger->info('Reading resource: user profile', ['userId' => $userId]); + if (! isset($this->users[$userId])) { + // Throwing an exception that Processor can turn into an error response + throw McpServerException::invalidParams("User profile not found for ID: {$userId}"); + } + + return $this->users[$userId]; + } + + /** + * Retrieves a list of all known user IDs. + * + * @return array List of user IDs. + */ + #[McpResource( + uri: 'user://list/ids', + name: 'user_id_list', + description: 'Provides a list of all available user IDs.', + mimeType: 'application/json' + )] + public function listUserIds(): array + { + $this->logger->info('Reading resource: user ID list'); + + return array_keys($this->users); + } + + /** + * Sends a welcome message to a user. + * (This is a placeholder - in a real app, it might queue an email) + * + * @param string $userId The ID of the user to message. + * @param string|null $customMessage An optional custom message part. + * @return array Status of the operation. + */ + #[McpTool(name: 'send_welcome')] + public function sendWelcomeMessage(string $userId, ?string $customMessage = null): array + { + $this->logger->info('Executing tool: send_welcome', ['userId' => $userId]); + if (! isset($this->users[$userId])) { + return ['success' => false, 'error' => "User ID {$userId} not found."]; + } + $user = $this->users[$userId]; + $message = "Welcome, {$user['name']}!"; + if ($customMessage) { + $message .= ' ' . $customMessage; + } + // Simulate sending + $this->logger->info("Simulated sending message to {$user['email']}: {$message}"); + + return ['success' => true, 'message_sent' => $message]; + } + + #[McpTool(name: 'test_tool_without_params')] + public function testToolWithoutParams() + { + return ['success' => true, 'message' => 'Test tool without params']; + } + + /** + * Generates a prompt to write a bio for a user. + * + * @param string $userId The user ID to generate the bio for. + * @param string $tone Desired tone (e.g., 'formal', 'casual'). + * @return array Prompt messages. + * + * @throws McpServerException If user not found. + */ + #[McpPrompt(name: 'generate_bio_prompt')] + public function generateBio( + #[CompletionProvider(provider: UserIdCompletionProvider::class)] + string $userId, + string $tone = 'professional' + ): array { + $this->logger->info('Executing prompt: generate_bio', ['userId' => $userId, 'tone' => $tone]); + if (! isset($this->users[$userId])) { + throw McpServerException::invalidParams("User not found for bio prompt: {$userId}"); + } + $user = $this->users[$userId]; + + return [ + ['role' => 'user', 'content' => "Write a short, {$tone} biography for {$user['name']} (Role: {$user['role']}, Email: {$user['email']}). Highlight their role within the system."], + ]; + } +} diff --git a/vendor/php-mcp/server/examples/02-discovery-http-userprofile/UserIdCompletionProvider.php b/vendor/php-mcp/server/examples/02-discovery-http-userprofile/UserIdCompletionProvider.php new file mode 100644 index 000000000..983494afe --- /dev/null +++ b/vendor/php-mcp/server/examples/02-discovery-http-userprofile/UserIdCompletionProvider.php @@ -0,0 +1,19 @@ + str_contains($userId, $currentValue)); + + return $filteredUserIds; + } +} diff --git a/vendor/php-mcp/server/examples/02-discovery-http-userprofile/server.php b/vendor/php-mcp/server/examples/02-discovery-http-userprofile/server.php new file mode 100644 index 000000000..30b2665c0 --- /dev/null +++ b/vendor/php-mcp/server/examples/02-discovery-http-userprofile/server.php @@ -0,0 +1,132 @@ +#!/usr/bin/env php +discover() scans for elements, + | and then $server->listen() starts the ReactPHP HTTP server. + | + | If you provided a `CacheInterface` implementation to the ServerBuilder, + | the discovery process will be cached, so you can comment out the + | discovery call after the first run to speed up subsequent runs. + | +*/ + +declare(strict_types=1); + +chdir(__DIR__); +require_once '../../vendor/autoload.php'; +require_once 'McpElements.php'; +require_once 'UserIdCompletionProvider.php'; + +use PhpMcp\Schema\ServerCapabilities; +use PhpMcp\Server\Defaults\BasicContainer; +use PhpMcp\Server\Server; +use PhpMcp\Server\Transports\HttpServerTransport; +use PhpMcp\Server\Transports\StreamableHttpServerTransport; +use Psr\Log\AbstractLogger; +use Psr\Log\LoggerInterface; + +class StderrLogger extends AbstractLogger +{ + public function log($level, \Stringable|string $message, array $context = []): void + { + fwrite(STDERR, sprintf("[%s][%s] %s %s\n", date('Y-m-d H:i:s'), strtoupper($level), $message, empty($context) ? '' : json_encode($context))); + } +} + +try { + $logger = new StderrLogger(); + $logger->info('Starting MCP HTTP User Profile Server...'); + + // --- Setup DI Container for DI in McpElements class --- + $container = new BasicContainer(); + $container->set(LoggerInterface::class, $logger); + + $server = Server::make() + ->withServerInfo('HTTP User Profiles', '1.0.0') + ->withCapabilities(ServerCapabilities::make(completions: true, logging: true)) + ->withLogger($logger) + ->withContainer($container) + ->withTool( + function (float $a, float $b, string $operation = 'add'): array { + $result = match ($operation) { + 'add' => $a + $b, + 'subtract' => $a - $b, + 'multiply' => $a * $b, + 'divide' => $b != 0 ? $a / $b : throw new \InvalidArgumentException('Cannot divide by zero'), + default => throw new \InvalidArgumentException("Unknown operation: {$operation}") + }; + + return [ + 'operation' => $operation, + 'operands' => [$a, $b], + 'result' => $result + ]; + }, + name: 'calculator', + description: 'Perform basic math operations (add, subtract, multiply, divide)' + ) + ->withResource( + function (): array { + $memoryUsage = memory_get_usage(true); + $memoryPeak = memory_get_peak_usage(true); + $uptime = time() - $_SERVER['REQUEST_TIME_FLOAT'] ?? time(); + $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'CLI'; + + return [ + 'server_time' => date('Y-m-d H:i:s'), + 'uptime_seconds' => $uptime, + 'memory_usage_mb' => round($memoryUsage / 1024 / 1024, 2), + 'memory_peak_mb' => round($memoryPeak / 1024 / 1024, 2), + 'php_version' => PHP_VERSION, + 'server_software' => $serverSoftware, + 'operating_system' => PHP_OS_FAMILY, + 'status' => 'healthy' + ]; + }, + uri: 'system://status', + name: 'system_status', + description: 'Current system status and runtime information', + mimeType: 'application/json' + ) + ->build(); + + $server->discover(__DIR__, ['.']); + + // $transport = new HttpServerTransport('127.0.0.1', 8080, 'mcp'); + $transport = new StreamableHttpServerTransport('127.0.0.1', 8080, 'mcp'); + + $server->listen($transport); + + $logger->info('Server listener stopped gracefully.'); + exit(0); +} catch (\Throwable $e) { + fwrite(STDERR, "[MCP SERVER CRITICAL ERROR]\n"); + fwrite(STDERR, 'Error: ' . $e->getMessage() . "\n"); + fwrite(STDERR, 'File: ' . $e->getFile() . ':' . $e->getLine() . "\n"); + fwrite(STDERR, $e->getTraceAsString() . "\n"); + exit(1); +} diff --git a/vendor/php-mcp/server/examples/03-manual-registration-stdio/SimpleHandlers.php b/vendor/php-mcp/server/examples/03-manual-registration-stdio/SimpleHandlers.php new file mode 100644 index 000000000..d602ee201 --- /dev/null +++ b/vendor/php-mcp/server/examples/03-manual-registration-stdio/SimpleHandlers.php @@ -0,0 +1,71 @@ +logger = $logger; + $this->logger->info('SimpleHandlers instantiated for manual registration example.'); + } + + /** + * A manually registered tool to echo input. + * + * @param string $text The text to echo. + * @return string The echoed text. + */ + public function echoText(string $text): string + { + $this->logger->info("Manual tool 'echo_text' called.", ['text' => $text]); + + return 'Echo: '.$text; + } + + /** + * A manually registered resource providing app version. + * + * @return string The application version. + */ + public function getAppVersion(): string + { + $this->logger->info("Manual resource 'app://version' read."); + + return $this->appVersion; + } + + /** + * A manually registered prompt template. + * + * @param string $userName The name of the user. + * @return array The prompt messages. + */ + public function greetingPrompt(string $userName): array + { + $this->logger->info("Manual prompt 'personalized_greeting' called.", ['userName' => $userName]); + + return [ + ['role' => 'user', 'content' => "Craft a personalized greeting for {$userName}."], + ]; + } + + /** + * A manually registered resource template. + * + * @param string $itemId The ID of the item. + * @return array Item details. + */ + public function getItemDetails(string $itemId): array + { + $this->logger->info("Manual template 'item://{itemId}' resolved.", ['itemId' => $itemId]); + + return ['id' => $itemId, 'name' => "Item {$itemId}", 'description' => "Details for item {$itemId} from manual template."]; + } +} diff --git a/vendor/php-mcp/server/examples/03-manual-registration-stdio/server.php b/vendor/php-mcp/server/examples/03-manual-registration-stdio/server.php new file mode 100644 index 000000000..0f64fe4f9 --- /dev/null +++ b/vendor/php-mcp/server/examples/03-manual-registration-stdio/server.php @@ -0,0 +1,79 @@ +#!/usr/bin/env php +discover() method is NOT called. + | +*/ + +declare(strict_types=1); + +chdir(__DIR__); +require_once '../../vendor/autoload.php'; +require_once './SimpleHandlers.php'; + +use Mcp\ManualStdioExample\SimpleHandlers; +use PhpMcp\Server\Defaults\BasicContainer; +use PhpMcp\Server\Server; +use PhpMcp\Server\Transports\StdioServerTransport; +use Psr\Log\AbstractLogger; +use Psr\Log\LoggerInterface; + +class StderrLogger extends AbstractLogger +{ + public function log($level, \Stringable|string $message, array $context = []): void + { + fwrite(STDERR, sprintf("[%s][%s] %s %s\n", date('Y-m-d H:i:s'), strtoupper($level), $message, empty($context) ? '' : json_encode($context))); + } +} + +try { + $logger = new StderrLogger(); + $logger->info('Starting MCP Manual Registration (Stdio) Server...'); + + $container = new BasicContainer(); + $container->set(LoggerInterface::class, $logger); + + $server = Server::make() + ->withServerInfo('Manual Reg Server', '1.0.0') + ->withLogger($logger) + ->withContainer($container) + ->withTool([SimpleHandlers::class, 'echoText'], 'echo_text') + ->withResource([SimpleHandlers::class, 'getAppVersion'], 'app://version', 'application_version', mimeType: 'text/plain') + ->withPrompt([SimpleHandlers::class, 'greetingPrompt'], 'personalized_greeting') + ->withResourceTemplate([SimpleHandlers::class, 'getItemDetails'], 'item://{itemId}/details', 'get_item_details', mimeType: 'application/json') + ->build(); + + $transport = new StdioServerTransport(); + $server->listen($transport); + + $logger->info('Server listener stopped gracefully.'); + exit(0); + +} catch (\Throwable $e) { + fwrite(STDERR, "[MCP SERVER CRITICAL ERROR]\n".$e."\n"); + exit(1); +} diff --git a/vendor/php-mcp/server/examples/04-combined-registration-http/DiscoveredElements.php b/vendor/php-mcp/server/examples/04-combined-registration-http/DiscoveredElements.php new file mode 100644 index 000000000..90192cc86 --- /dev/null +++ b/vendor/php-mcp/server/examples/04-combined-registration-http/DiscoveredElements.php @@ -0,0 +1,32 @@ +logger = $logger; + } + + /** + * A manually registered tool. + * + * @param string $user The user to greet. + * @return string Greeting. + */ + public function manualGreeter(string $user): string + { + $this->logger->info("Manual tool 'manual_greeter' called for {$user}"); + + return "Hello {$user}, from manual registration!"; + } + + /** + * Manually registered resource that overrides a discovered one. + * + * @return string Content. + */ + public function getPriorityConfigManual(): string + { + $this->logger->info("Manual resource 'config://priority' read."); + + return 'Manual Priority Config: HIGH (overrides discovered)'; + } +} diff --git a/vendor/php-mcp/server/examples/04-combined-registration-http/server.php b/vendor/php-mcp/server/examples/04-combined-registration-http/server.php new file mode 100644 index 000000000..57a9cfea9 --- /dev/null +++ b/vendor/php-mcp/server/examples/04-combined-registration-http/server.php @@ -0,0 +1,93 @@ +#!/usr/bin/env php +build(). + | Then, $server->discover() scans for attributed elements. + | +*/ + +declare(strict_types=1); + +chdir(__DIR__); +require_once '../../vendor/autoload.php'; +require_once './DiscoveredElements.php'; +require_once './ManualHandlers.php'; + +use Mcp\CombinedHttpExample\Manual\ManualHandlers; +use PhpMcp\Server\Defaults\BasicContainer; +use PhpMcp\Server\Server; +use PhpMcp\Server\Transports\HttpServerTransport; +use Psr\Log\AbstractLogger; +use Psr\Log\LoggerInterface; + +class StderrLogger extends AbstractLogger +{ + public function log($level, \Stringable|string $message, array $context = []): void + { + fwrite(STDERR, sprintf("[%s][%s] %s %s\n", date('Y-m-d H:i:s'), strtoupper($level), $message, empty($context) ? '' : json_encode($context))); + } +} + +try { + $logger = new StderrLogger(); + $logger->info('Starting MCP Combined Registration (HTTP) Server...'); + + $container = new BasicContainer(); + $container->set(LoggerInterface::class, $logger); // ManualHandlers needs LoggerInterface + + $server = Server::make() + ->withServerInfo('Combined HTTP Server', '1.0.0') + ->withLogger($logger) + ->withContainer($container) + ->withTool([ManualHandlers::class, 'manualGreeter']) + ->withResource( + [ManualHandlers::class, 'getPriorityConfigManual'], + 'config://priority', + 'priority_config_manual', + ) + ->build(); + + // Now, run discovery. Discovered elements will be added. + // If 'config://priority' was discovered, the manual one takes precedence. + $server->discover(__DIR__, scanDirs: ['.']); + + $transport = new HttpServerTransport('127.0.0.1', 8081, 'mcp_combined'); + + $server->listen($transport); + + $logger->info('Server listener stopped gracefully.'); + exit(0); + +} catch (\Throwable $e) { + fwrite(STDERR, "[MCP SERVER CRITICAL ERROR]\n".$e."\n"); + exit(1); +} diff --git a/vendor/php-mcp/server/examples/05-stdio-env-variables/EnvToolHandler.php b/vendor/php-mcp/server/examples/05-stdio-env-variables/EnvToolHandler.php new file mode 100644 index 000000000..271efae5c --- /dev/null +++ b/vendor/php-mcp/server/examples/05-stdio-env-variables/EnvToolHandler.php @@ -0,0 +1,45 @@ + 'debug', + 'processed_input' => strtoupper($input), + 'message' => 'Processed in DEBUG mode.', + ]; + } elseif ($appMode === 'production') { + return [ + 'mode' => 'production', + 'processed_input_length' => strlen($input), + 'message' => 'Processed in PRODUCTION mode (summary only).', + ]; + } else { + return [ + 'mode' => $appMode ?: 'default', + 'original_input' => $input, + 'message' => 'Processed in default mode (APP_MODE not recognized or not set).', + ]; + } + } +} diff --git a/vendor/php-mcp/server/examples/05-stdio-env-variables/server.php b/vendor/php-mcp/server/examples/05-stdio-env-variables/server.php new file mode 100644 index 000000000..774b7c78a --- /dev/null +++ b/vendor/php-mcp/server/examples/05-stdio-env-variables/server.php @@ -0,0 +1,73 @@ +#!/usr/bin/env php +info('Starting MCP Stdio Environment Variable Example Server...'); + + $server = Server::make() + ->withServerInfo('Env Var Server', '1.0.0') + ->withLogger($logger) + ->build(); + + $server->discover(__DIR__, ['.']); + + $transport = new StdioServerTransport(); + $server->listen($transport); + + $logger->info('Server listener stopped gracefully.'); + exit(0); + +} catch (\Throwable $e) { + fwrite(STDERR, "[MCP SERVER CRITICAL ERROR]\n".$e."\n"); + exit(1); +} diff --git a/vendor/php-mcp/server/examples/06-custom-dependencies-stdio/McpTaskHandlers.php b/vendor/php-mcp/server/examples/06-custom-dependencies-stdio/McpTaskHandlers.php new file mode 100644 index 000000000..7de78af63 --- /dev/null +++ b/vendor/php-mcp/server/examples/06-custom-dependencies-stdio/McpTaskHandlers.php @@ -0,0 +1,87 @@ +taskRepo = $taskRepo; + $this->statsService = $statsService; + $this->logger = $logger; + $this->logger->info('McpTaskHandlers instantiated with dependencies.'); + } + + /** + * Adds a new task for a given user. + * + * @param string $userId The ID of the user. + * @param string $description The task description. + * @return array The created task details. + */ + #[McpTool(name: 'add_task')] + public function addTask(string $userId, string $description): array + { + $this->logger->info("Tool 'add_task' invoked", ['userId' => $userId]); + + return $this->taskRepo->addTask($userId, $description); + } + + /** + * Lists pending tasks for a specific user. + * + * @param string $userId The ID of the user. + * @return array A list of tasks. + */ + #[McpTool(name: 'list_user_tasks')] + public function listUserTasks(string $userId): array + { + $this->logger->info("Tool 'list_user_tasks' invoked", ['userId' => $userId]); + + return $this->taskRepo->getTasksForUser($userId); + } + + /** + * Marks a task as complete. + * + * @param int $taskId The ID of the task to complete. + * @return array Status of the operation. + */ + #[McpTool(name: 'complete_task')] + public function completeTask(int $taskId): array + { + $this->logger->info("Tool 'complete_task' invoked", ['taskId' => $taskId]); + $success = $this->taskRepo->completeTask($taskId); + + return ['success' => $success, 'message' => $success ? "Task {$taskId} completed." : "Task {$taskId} not found."]; + } + + /** + * Provides current system statistics. + * + * @return array System statistics. + */ + #[McpResource(uri: 'stats://system/overview', name: 'system_stats', mimeType: 'application/json')] + public function getSystemStatistics(): array + { + $this->logger->info("Resource 'stats://system/overview' invoked"); + + return $this->statsService->getSystemStats(); + } +} diff --git a/vendor/php-mcp/server/examples/06-custom-dependencies-stdio/Services.php b/vendor/php-mcp/server/examples/06-custom-dependencies-stdio/Services.php new file mode 100644 index 000000000..47d7de630 --- /dev/null +++ b/vendor/php-mcp/server/examples/06-custom-dependencies-stdio/Services.php @@ -0,0 +1,102 @@ +logger = $logger; + // Add some initial tasks + $this->addTask('user1', 'Buy groceries'); + $this->addTask('user1', 'Write MCP example'); + $this->addTask('user2', 'Review PR'); + } + + public function addTask(string $userId, string $description): array + { + $task = [ + 'id' => $this->nextTaskId++, + 'userId' => $userId, + 'description' => $description, + 'completed' => false, + 'createdAt' => date('c'), + ]; + $this->tasks[$task['id']] = $task; + $this->logger->info('Task added', ['id' => $task['id'], 'user' => $userId]); + + return $task; + } + + public function getTasksForUser(string $userId): array + { + return array_values(array_filter($this->tasks, fn ($task) => $task['userId'] === $userId && ! $task['completed'])); + } + + public function getAllTasks(): array + { + return array_values($this->tasks); + } + + public function completeTask(int $taskId): bool + { + if (isset($this->tasks[$taskId])) { + $this->tasks[$taskId]['completed'] = true; + $this->logger->info('Task completed', ['id' => $taskId]); + + return true; + } + + return false; + } +} + +interface StatsServiceInterface +{ + public function getSystemStats(): array; +} + +class SystemStatsService implements StatsServiceInterface +{ + private TaskRepositoryInterface $taskRepository; + + public function __construct(TaskRepositoryInterface $taskRepository) + { + $this->taskRepository = $taskRepository; + } + + public function getSystemStats(): array + { + $allTasks = $this->taskRepository->getAllTasks(); + $completed = count(array_filter($allTasks, fn ($task) => $task['completed'])); + $pending = count($allTasks) - $completed; + + return [ + 'total_tasks' => count($allTasks), + 'completed_tasks' => $completed, + 'pending_tasks' => $pending, + 'server_uptime_seconds' => time() - $_SERVER['REQUEST_TIME_FLOAT'], // Approx uptime for CLI script + ]; + } +} diff --git a/vendor/php-mcp/server/examples/06-custom-dependencies-stdio/server.php b/vendor/php-mcp/server/examples/06-custom-dependencies-stdio/server.php new file mode 100644 index 000000000..f8a630f84 --- /dev/null +++ b/vendor/php-mcp/server/examples/06-custom-dependencies-stdio/server.php @@ -0,0 +1,103 @@ +#!/usr/bin/env php +withContainer($container)`. + | - The DI container is set up with bindings for service interfaces to + | their concrete implementations (e.g., TaskRepositoryInterface -> InMemoryTaskRepository). + | - The `McpTaskHandlers` class receives its dependencies (TaskRepositoryInterface, + | StatsServiceInterface, LoggerInterface) via constructor injection, resolved by + | the DI container when the Processor needs an instance of McpTaskHandlers. + | - This example uses attribute-based discovery via `$server->discover()`. + | - It runs using the STDIO transport. + | + | To Use: + | 1. Run this script: `php server.php` (from this directory) + | 2. Configure your MCP Client (e.g., Cursor) for this server: + | + | { + | "mcpServers": { + | "php-stdio-deps-taskmgr": { + | "command": "php", + | "args": ["/full/path/to/examples/06-custom-dependencies-stdio/server.php"] + | } + | } + | } + | + | Interact with tools like 'add_task', 'list_user_tasks', 'complete_task' + | and read the resource 'stats://system/overview'. + | +*/ + +declare(strict_types=1); + +chdir(__DIR__); +require_once '../../vendor/autoload.php'; +require_once './Services.php'; +require_once './McpTaskHandlers.php'; + +use Mcp\DependenciesStdioExample\Services; +use PhpMcp\Server\Defaults\BasicContainer; +use PhpMcp\Server\Server; +use PhpMcp\Server\Transports\StdioServerTransport; +use Psr\Log\AbstractLogger; +use Psr\Log\LoggerInterface; + +class StderrLogger extends AbstractLogger +{ + public function log($level, \Stringable|string $message, array $context = []): void + { + fwrite(STDERR, sprintf("[%s][%s] %s %s\n", date('Y-m-d H:i:s'), strtoupper($level), $message, empty($context) ? '' : json_encode($context))); + } +} + +try { + $logger = new StderrLogger(); + $logger->info('Starting MCP Custom Dependencies (Stdio) Server...'); + + $container = new BasicContainer(); + $container->set(LoggerInterface::class, $logger); + + $taskRepo = new Services\InMemoryTaskRepository($logger); + $container->set(Services\TaskRepositoryInterface::class, $taskRepo); + + $statsService = new Services\SystemStatsService($taskRepo); + $container->set(Services\StatsServiceInterface::class, $statsService); + + $server = Server::make() + ->withServerInfo('Task Manager Server', '1.0.0') + ->withLogger($logger) + ->withContainer($container) + ->build(); + + $server->discover(__DIR__, ['.']); + + $transport = new StdioServerTransport(); + $server->listen($transport); + + $logger->info('Server listener stopped gracefully.'); + exit(0); +} catch (\Throwable $e) { + fwrite(STDERR, "[MCP SERVER CRITICAL ERROR]\n"); + fwrite(STDERR, 'Error: ' . $e->getMessage() . "\n"); + fwrite(STDERR, 'File: ' . $e->getFile() . ':' . $e->getLine() . "\n"); + fwrite(STDERR, $e->getTraceAsString() . "\n"); + exit(1); +} diff --git a/vendor/php-mcp/server/examples/07-complex-tool-schema-http/EventTypes.php b/vendor/php-mcp/server/examples/07-complex-tool-schema-http/EventTypes.php new file mode 100644 index 000000000..0cb4dd14c --- /dev/null +++ b/vendor/php-mcp/server/examples/07-complex-tool-schema-http/EventTypes.php @@ -0,0 +1,18 @@ +logger = $logger; + } + + /** + * Schedules a new event. + * The inputSchema for this tool will reflect all parameter types and defaults. + * + * @param string $title The title of the event. + * @param string $date The date of the event (YYYY-MM-DD). + * @param EventType $type The type of event. + * @param string|null $time The time of the event (HH:MM), optional. + * @param EventPriority $priority The priority of the event. Defaults to Normal. + * @param string[]|null $attendees An optional list of attendee email addresses. + * @param bool $sendInvites Send calendar invites to attendees? Defaults to true if attendees are provided. + * @return array Confirmation of the scheduled event. + */ + #[McpTool(name: 'schedule_event')] + public function scheduleEvent( + string $title, + string $date, + EventType $type, + ?string $time = null, // Optional, nullable + EventPriority $priority = EventPriority::Normal, // Optional with enum default + ?array $attendees = null, // Optional array of strings, nullable + bool $sendInvites = true // Optional with default + ): array { + $this->logger->info("Tool 'schedule_event' called", compact('title', 'date', 'type', 'time', 'priority', 'attendees', 'sendInvites')); + + // Simulate scheduling logic + $eventDetails = [ + 'title' => $title, + 'date' => $date, + 'type' => $type->value, // Use enum value + 'time' => $time ?? 'All day', + 'priority' => $priority->name, // Use enum name + 'attendees' => $attendees ?? [], + 'invites_will_be_sent' => ($attendees && $sendInvites), + ]; + + // In a real app, this would interact with a calendar service + $this->logger->info('Event scheduled', ['details' => $eventDetails]); + + return [ + 'success' => true, + 'message' => "Event '{$title}' scheduled successfully for {$date}.", + 'event_details' => $eventDetails, + ]; + } +} diff --git a/vendor/php-mcp/server/examples/07-complex-tool-schema-http/server.php b/vendor/php-mcp/server/examples/07-complex-tool-schema-http/server.php new file mode 100644 index 000000000..c7d7a77df --- /dev/null +++ b/vendor/php-mcp/server/examples/07-complex-tool-schema-http/server.php @@ -0,0 +1,94 @@ +#!/usr/bin/env php +info('Starting MCP Complex Schema HTTP Server...'); + + $container = new BasicContainer(); + $container->set(LoggerInterface::class, $logger); + + $server = Server::make() + ->withServerInfo('Event Scheduler Server', '1.0.0') + ->withLogger($logger) + ->withContainer($container) + ->build(); + + $server->discover(__DIR__, ['.']); + + $transport = new HttpServerTransport('127.0.0.1', 8082, 'mcp_scheduler'); + $server->listen($transport); + + $logger->info('Server listener stopped gracefully.'); + exit(0); + +} catch (\Throwable $e) { + fwrite(STDERR, "[MCP SERVER CRITICAL ERROR]\n".$e."\n"); + exit(1); +} diff --git a/vendor/php-mcp/server/examples/08-schema-showcase-streamable/SchemaShowcaseElements.php b/vendor/php-mcp/server/examples/08-schema-showcase-streamable/SchemaShowcaseElements.php new file mode 100644 index 000000000..899f7fd26 --- /dev/null +++ b/vendor/php-mcp/server/examples/08-schema-showcase-streamable/SchemaShowcaseElements.php @@ -0,0 +1,434 @@ + strtoupper($text), + 'lowercase' => strtolower($text), + 'title' => ucwords(strtolower($text)), + 'sentence' => ucfirst(strtolower($text)), + default => $text + }; + + return [ + 'original' => $text, + 'formatted' => $formatted, + 'length' => strlen($text), + 'format_applied' => $format + ]; + } + + /** + * Performs mathematical operations with numeric constraints. + * + * Demonstrates: METHOD-LEVEL Schema + */ + #[McpTool(name: 'calculate_range')] + #[Schema( + type: 'object', + properties: [ + 'first' => [ + 'type' => 'number', + 'description' => 'First number (must be between 0 and 1000)', + 'minimum' => 0, + 'maximum' => 1000 + ], + 'second' => [ + 'type' => 'number', + 'description' => 'Second number (must be between 0 and 1000)', + 'minimum' => 0, + 'maximum' => 1000 + ], + 'operation' => [ + 'type' => 'string', + 'description' => 'Operation to perform', + 'enum' => ['add', 'subtract', 'multiply', 'divide', 'power'] + ], + 'precision' => [ + 'type' => 'integer', + 'description' => 'Decimal precision (must be multiple of 2, between 0-10)', + 'minimum' => 0, + 'maximum' => 10, + 'multipleOf' => 2 + ] + ], + required: ['first', 'second', 'operation'], + )] + public function calculateRange(float $first, float $second, string $operation, int $precision = 2): array + { + fwrite(STDERR, "Calculate range tool called: $first $operation $second (precision: $precision)\n"); + + $result = match ($operation) { + 'add' => $first + $second, + 'subtract' => $first - $second, + 'multiply' => $first * $second, + 'divide' => $second != 0 ? $first / $second : null, + 'power' => pow($first, $second), + default => null + }; + + if ($result === null) { + return [ + 'error' => $operation === 'divide' ? 'Division by zero' : 'Invalid operation', + 'inputs' => compact('first', 'second', 'operation', 'precision') + ]; + } + + return [ + 'result' => round($result, $precision), + 'operation' => "$first $operation $second", + 'precision' => $precision, + 'within_bounds' => $result >= 0 && $result <= 1000000 + ]; + } + + /** + * Processes user profile data with object schema validation. + * Demonstrates: object properties, required fields, additionalProperties. + */ + #[McpTool( + name: 'validate_profile', + description: 'Validates and processes user profile data with strict schema requirements.' + )] + public function validateProfile( + #[Schema( + type: 'object', + description: 'User profile information', + properties: [ + 'name' => [ + 'type' => 'string', + 'minLength' => 2, + 'maxLength' => 50, + 'description' => 'Full name' + ], + 'email' => [ + 'type' => 'string', + 'format' => 'email', + 'description' => 'Valid email address' + ], + 'age' => [ + 'type' => 'integer', + 'minimum' => 13, + 'maximum' => 120, + 'description' => 'Age in years' + ], + 'role' => [ + 'type' => 'string', + 'enum' => ['user', 'admin', 'moderator', 'guest'], + 'description' => 'User role' + ], + 'preferences' => [ + 'type' => 'object', + 'properties' => [ + 'notifications' => ['type' => 'boolean'], + 'theme' => ['type' => 'string', 'enum' => ['light', 'dark', 'auto']] + ], + 'additionalProperties' => false + ] + ], + required: ['name', 'email', 'age'], + additionalProperties: true + )] + array $profile + ): array { + fwrite(STDERR, "Validate profile tool called with: " . json_encode($profile) . "\n"); + + $errors = []; + $warnings = []; + + // Additional business logic validation + if (isset($profile['age']) && $profile['age'] < 18 && ($profile['role'] ?? 'user') === 'admin') { + $errors[] = 'Admin role requires age 18 or older'; + } + + if (isset($profile['email']) && !filter_var($profile['email'], FILTER_VALIDATE_EMAIL)) { + $errors[] = 'Invalid email format'; + } + + if (!isset($profile['role'])) { + $warnings[] = 'No role specified, defaulting to "user"'; + $profile['role'] = 'user'; + } + + return [ + 'valid' => empty($errors), + 'profile' => $profile, + 'errors' => $errors, + 'warnings' => $warnings, + 'processed_at' => date('Y-m-d H:i:s') + ]; + } + + /** + * Manages a list of items with array constraints. + * Demonstrates: array items, minItems, maxItems, uniqueItems. + */ + #[McpTool( + name: 'manage_list', + description: 'Manages a list of items with size and uniqueness constraints.' + )] + public function manageList( + #[Schema( + type: 'array', + description: 'List of items to manage (2-10 unique strings)', + items: [ + 'type' => 'string', + 'minLength' => 1, + 'maxLength' => 30 + ], + minItems: 2, + maxItems: 10, + uniqueItems: true + )] + array $items, + + #[Schema( + type: 'string', + description: 'Action to perform on the list', + enum: ['sort', 'reverse', 'shuffle', 'deduplicate', 'filter_short', 'filter_long'] + )] + string $action = 'sort' + ): array { + fwrite(STDERR, "Manage list tool called with " . count($items) . " items, action: $action\n"); + + $original = $items; + $processed = $items; + + switch ($action) { + case 'sort': + sort($processed); + break; + case 'reverse': + $processed = array_reverse($processed); + break; + case 'shuffle': + shuffle($processed); + break; + case 'deduplicate': + $processed = array_unique($processed); + break; + case 'filter_short': + $processed = array_filter($processed, fn($item) => strlen($item) <= 10); + break; + case 'filter_long': + $processed = array_filter($processed, fn($item) => strlen($item) > 10); + break; + } + + return [ + 'original_count' => count($original), + 'processed_count' => count($processed), + 'action' => $action, + 'original' => $original, + 'processed' => array_values($processed), // Re-index array + 'stats' => [ + 'average_length' => count($processed) > 0 ? round(array_sum(array_map('strlen', $processed)) / count($processed), 2) : 0, + 'shortest' => count($processed) > 0 ? min(array_map('strlen', $processed)) : 0, + 'longest' => count($processed) > 0 ? max(array_map('strlen', $processed)) : 0, + ] + ]; + } + + /** + * Generates configuration with format validation. + * Demonstrates: format constraints (date-time, uri, etc). + */ + #[McpTool( + name: 'generate_config', + description: 'Generates configuration with format-validated inputs.' + )] + public function generateConfig( + #[Schema( + type: 'string', + description: 'Application name (alphanumeric with hyphens)', + pattern: '^[a-zA-Z0-9\-]+$', + minLength: 3, + maxLength: 20 + )] + string $appName, + + #[Schema( + type: 'string', + description: 'Valid URL for the application', + format: 'uri' + )] + string $baseUrl, + + #[Schema( + type: 'string', + description: 'Environment type', + enum: ['development', 'staging', 'production'] + )] + string $environment = 'development', + + #[Schema( + type: 'boolean', + description: 'Enable debug mode' + )] + bool $debug = true, + + #[Schema( + type: 'integer', + description: 'Port number (1024-65535)', + minimum: 1024, + maximum: 65535 + )] + int $port = 8080 + ): array { + fwrite(STDERR, "Generate config tool called for app: $appName\n"); + + $config = [ + 'app' => [ + 'name' => $appName, + 'env' => $environment, + 'debug' => $debug, + 'url' => $baseUrl, + 'port' => $port, + ], + 'generated_at' => date('c'), // ISO 8601 format + 'version' => '1.0.0', + 'features' => [ + 'logging' => $environment !== 'production' || $debug, + 'caching' => $environment === 'production', + 'analytics' => $environment === 'production', + 'rate_limiting' => $environment !== 'development', + ] + ]; + + return [ + 'success' => true, + 'config' => $config, + 'validation' => [ + 'app_name_valid' => preg_match('/^[a-zA-Z0-9\-]+$/', $appName) === 1, + 'url_valid' => filter_var($baseUrl, FILTER_VALIDATE_URL) !== false, + 'port_in_range' => $port >= 1024 && $port <= 65535, + ] + ]; + } + + /** + * Processes time-based data with date-time format validation. + * Demonstrates: date-time format, exclusiveMinimum, exclusiveMaximum. + */ + #[McpTool( + name: 'schedule_event', + description: 'Schedules an event with time validation and constraints.' + )] + public function scheduleEvent( + #[Schema( + type: 'string', + description: 'Event title (3-50 characters)', + minLength: 3, + maxLength: 50 + )] + string $title, + + #[Schema( + type: 'string', + description: 'Event start time in ISO 8601 format', + format: 'date-time' + )] + string $startTime, + + #[Schema( + type: 'number', + description: 'Duration in hours (minimum 0.5, maximum 24)', + minimum: 0.5, + maximum: 24, + multipleOf: 0.5 + )] + float $durationHours, + + #[Schema( + type: 'string', + description: 'Event priority level', + enum: ['low', 'medium', 'high', 'urgent'] + )] + string $priority = 'medium', + + #[Schema( + type: 'array', + description: 'List of attendee email addresses', + items: [ + 'type' => 'string', + 'format' => 'email' + ], + maxItems: 20 + )] + array $attendees = [] + ): array { + fwrite(STDERR, "Schedule event tool called: $title at $startTime\n"); + + $start = DateTime::createFromFormat(DateTime::ISO8601, $startTime); + if (!$start) { + $start = DateTime::createFromFormat('Y-m-d\TH:i:s\Z', $startTime); + } + + if (!$start) { + return [ + 'success' => false, + 'error' => 'Invalid date-time format. Use ISO 8601 format.', + 'example' => '2024-01-15T14:30:00Z' + ]; + } + + $end = clone $start; + $end->add(new DateInterval('PT' . ($durationHours * 60) . 'M')); + + $event = [ + 'id' => uniqid('event_'), + 'title' => $title, + 'start_time' => $start->format('c'), + 'end_time' => $end->format('c'), + 'duration_hours' => $durationHours, + 'priority' => $priority, + 'attendees' => $attendees, + 'created_at' => date('c') + ]; + + return [ + 'success' => true, + 'event' => $event, + 'info' => [ + 'attendee_count' => count($attendees), + 'is_all_day' => $durationHours >= 24, + 'is_future' => $start > new DateTime(), + 'timezone_note' => 'Times are in UTC' + ] + ]; + } +} diff --git a/vendor/php-mcp/server/examples/08-schema-showcase-streamable/server.php b/vendor/php-mcp/server/examples/08-schema-showcase-streamable/server.php new file mode 100644 index 000000000..45761d7d0 --- /dev/null +++ b/vendor/php-mcp/server/examples/08-schema-showcase-streamable/server.php @@ -0,0 +1,77 @@ +#!/usr/bin/env php +info('Starting MCP Schema Showcase Server...'); + + $server = Server::make() + ->withServerInfo('Schema Showcase', '1.0.0') + ->withLogger($logger) + ->build(); + + $server->discover(__DIR__, ['.']); + + $transport = new StreamableHttpServerTransport('127.0.0.1', 8080, 'mcp'); + + $server->listen($transport); + + $logger->info('Server listener stopped gracefully.'); + exit(0); +} catch (\Throwable $e) { + fwrite(STDERR, "[MCP SERVER CRITICAL ERROR]\n"); + fwrite(STDERR, 'Error: ' . $e->getMessage() . "\n"); + fwrite(STDERR, 'File: ' . $e->getFile() . ':' . $e->getLine() . "\n"); + fwrite(STDERR, $e->getTraceAsString() . "\n"); + exit(1); +} diff --git a/vendor/php-mcp/server/phpunit.xml b/vendor/php-mcp/server/phpunit.xml new file mode 100644 index 000000000..35cdfbb3a --- /dev/null +++ b/vendor/php-mcp/server/phpunit.xml @@ -0,0 +1,17 @@ + + + + + ./tests + + + + + src + + + diff --git a/vendor/php-mcp/server/src/Attributes/CompletionProvider.php b/vendor/php-mcp/server/src/Attributes/CompletionProvider.php new file mode 100644 index 000000000..29ca61f10 --- /dev/null +++ b/vendor/php-mcp/server/src/Attributes/CompletionProvider.php @@ -0,0 +1,27 @@ +|null $providerClass + * @param class-string|CompletionProviderInterface|null $provider If a class-string, it will be resolved from the container at the point of use. + */ + public function __construct( + public ?string $providerClass = null, + public string|CompletionProviderInterface|null $provider = null, + public ?array $values = null, + public ?string $enum = null, + ) { + if (count(array_filter([$provider, $values, $enum])) !== 1) { + throw new \InvalidArgumentException('Only one of provider, values, or enum can be set'); + } + } +} diff --git a/vendor/php-mcp/server/src/Attributes/McpPrompt.php b/vendor/php-mcp/server/src/Attributes/McpPrompt.php new file mode 100644 index 000000000..4e6d16722 --- /dev/null +++ b/vendor/php-mcp/server/src/Attributes/McpPrompt.php @@ -0,0 +1,23 @@ + [schema array], ...] + public ?array $required = null; // [propertyName, ...] + public bool|array|null $additionalProperties = null; // true, false, or a schema array + + /** + * @param array|null $definition A complete JSON schema array. If provided, other parameters are ignored. + * @param Type|null $type The JSON schema type. + * @param string|null $description Description of the element. + * @param array|null $enum Allowed enum values. + * @param string|null $format String format (e.g., 'date-time', 'email'). + * @param int|null $minLength Minimum length for strings. + * @param int|null $maxLength Maximum length for strings. + * @param string|null $pattern Regex pattern for strings. + * @param int|float|null $minimum Minimum value for numbers/integers. + * @param int|float|null $maximum Maximum value for numbers/integers. + * @param bool|null $exclusiveMinimum Exclusive minimum. + * @param bool|null $exclusiveMaximum Exclusive maximum. + * @param int|float|null $multipleOf Must be a multiple of this value. + * @param array|null $items JSON Schema for items if type is 'array'. + * @param int|null $minItems Minimum items for an array. + * @param int|null $maxItems Maximum items for an array. + * @param bool|null $uniqueItems Whether array items must be unique. + * @param array|null $properties Property definitions if type is 'object'. [name => schema_array]. + * @param array|null $required List of required properties for an object. + * @param bool|array|null $additionalProperties Policy for additional properties in an object. + */ + public function __construct( + ?array $definition = null, + ?string $type = null, + ?string $description = null, + ?array $enum = null, + ?string $format = null, + ?int $minLength = null, + ?int $maxLength = null, + ?string $pattern = null, + int|float|null $minimum = null, + int|float|null $maximum = null, + ?bool $exclusiveMinimum = null, + ?bool $exclusiveMaximum = null, + int|float|null $multipleOf = null, + ?array $items = null, + ?int $minItems = null, + ?int $maxItems = null, + ?bool $uniqueItems = null, + ?array $properties = null, + ?array $required = null, + bool|array|null $additionalProperties = null + ) { + if ($definition !== null) { + $this->definition = $definition; + } else { + $this->type = $type; + $this->description = $description; + $this->enum = $enum; + $this->format = $format; + $this->minLength = $minLength; + $this->maxLength = $maxLength; + $this->pattern = $pattern; + $this->minimum = $minimum; + $this->maximum = $maximum; + $this->exclusiveMinimum = $exclusiveMinimum; + $this->exclusiveMaximum = $exclusiveMaximum; + $this->multipleOf = $multipleOf; + $this->items = $items; + $this->minItems = $minItems; + $this->maxItems = $maxItems; + $this->uniqueItems = $uniqueItems; + $this->properties = $properties; + $this->required = $required; + $this->additionalProperties = $additionalProperties; + } + } + + /** + * Converts the attribute's definition to a JSON schema array. + */ + public function toArray(): array + { + if ($this->definition !== null) { + return [ + 'definition' => $this->definition, + ]; + } + + $schema = []; + if ($this->type !== null) { + $schema['type'] = $this->type; + } + if ($this->description !== null) { + $schema['description'] = $this->description; + } + if ($this->enum !== null) { + $schema['enum'] = $this->enum; + } + if ($this->format !== null) { + $schema['format'] = $this->format; + } + + // String + if ($this->minLength !== null) { + $schema['minLength'] = $this->minLength; + } + if ($this->maxLength !== null) { + $schema['maxLength'] = $this->maxLength; + } + if ($this->pattern !== null) { + $schema['pattern'] = $this->pattern; + } + + // Numeric + if ($this->minimum !== null) { + $schema['minimum'] = $this->minimum; + } + if ($this->maximum !== null) { + $schema['maximum'] = $this->maximum; + } + if ($this->exclusiveMinimum !== null) { + $schema['exclusiveMinimum'] = $this->exclusiveMinimum; + } + if ($this->exclusiveMaximum !== null) { + $schema['exclusiveMaximum'] = $this->exclusiveMaximum; + } + if ($this->multipleOf !== null) { + $schema['multipleOf'] = $this->multipleOf; + } + + // Array + if ($this->items !== null) { + $schema['items'] = $this->items; + } + if ($this->minItems !== null) { + $schema['minItems'] = $this->minItems; + } + if ($this->maxItems !== null) { + $schema['maxItems'] = $this->maxItems; + } + if ($this->uniqueItems !== null) { + $schema['uniqueItems'] = $this->uniqueItems; + } + + // Object + if ($this->properties !== null) { + $schema['properties'] = $this->properties; + } + if ($this->required !== null) { + $schema['required'] = $this->required; + } + if ($this->additionalProperties !== null) { + $schema['additionalProperties'] = $this->additionalProperties; + } + + return $schema; + } +} diff --git a/vendor/php-mcp/server/src/Configuration.php b/vendor/php-mcp/server/src/Configuration.php new file mode 100644 index 000000000..b0af28064 --- /dev/null +++ b/vendor/php-mcp/server/src/Configuration.php @@ -0,0 +1,41 @@ + Resolves on successful send/queue, rejects on specific send error. + */ + public function sendMessage(Message $message, string $sessionId, array $context = []): PromiseInterface; + + /** + * Stops the transport listener gracefully and closes all active connections. + * MUST eventually emit a 'close' event for the transport itself. + * Individual client disconnects should emit 'client_disconnected' events. + */ + public function close(): void; +} diff --git a/vendor/php-mcp/server/src/Contracts/SessionHandlerInterface.php b/vendor/php-mcp/server/src/Contracts/SessionHandlerInterface.php new file mode 100644 index 000000000..e6fe8dc25 --- /dev/null +++ b/vendor/php-mcp/server/src/Contracts/SessionHandlerInterface.php @@ -0,0 +1,38 @@ + + */ + public function dequeueMessages(): array; + + /** + * Check if there are any messages in the queue. + */ + public function hasQueuedMessages(): bool; + + /** + * Get the session handler instance. + * + * @return SessionHandlerInterface + */ + public function getHandler(): SessionHandlerInterface; +} diff --git a/vendor/php-mcp/server/src/Defaults/ArrayCache.php b/vendor/php-mcp/server/src/Defaults/ArrayCache.php new file mode 100644 index 000000000..348d8c0db --- /dev/null +++ b/vendor/php-mcp/server/src/Defaults/ArrayCache.php @@ -0,0 +1,110 @@ +has($key)) { + return $default; + } + + return $this->store[$key]; + } + + public function set(string $key, mixed $value, DateInterval|int|null $ttl = null): bool + { + $this->store[$key] = $value; + $this->expiries[$key] = $this->calculateExpiry($ttl); + + return true; + } + + public function delete(string $key): bool + { + unset($this->store[$key], $this->expiries[$key]); + + return true; + } + + public function clear(): bool + { + $this->store = []; + $this->expiries = []; + + return true; + } + + public function getMultiple(iterable $keys, mixed $default = null): iterable + { + $result = []; + foreach ($keys as $key) { + $result[$key] = $this->get($key, $default); + } + + return $result; + } + + public function setMultiple(iterable $values, DateInterval|int|null $ttl = null): bool + { + $expiry = $this->calculateExpiry($ttl); + foreach ($values as $key => $value) { + $this->store[$key] = $value; + $this->expiries[$key] = $expiry; + } + + return true; + } + + public function deleteMultiple(iterable $keys): bool + { + foreach ($keys as $key) { + unset($this->store[$key], $this->expiries[$key]); + } + + return true; + } + + public function has(string $key): bool + { + if (! isset($this->store[$key])) { + return false; + } + // Check expiry + if (isset($this->expiries[$key]) && $this->expiries[$key] !== null && time() >= $this->expiries[$key]) { + $this->delete($key); + + return false; + } + + return true; + } + + private function calculateExpiry(DateInterval|int|null $ttl): ?int + { + if ($ttl === null) { + return null; // No expiry + } + if (is_int($ttl)) { + return time() + $ttl; + } + if ($ttl instanceof DateInterval) { + return (new DateTime())->add($ttl)->getTimestamp(); + } + + // Invalid TTL type, treat as no expiry + return null; + } +} diff --git a/vendor/php-mcp/server/src/Defaults/BasicContainer.php b/vendor/php-mcp/server/src/Defaults/BasicContainer.php new file mode 100644 index 000000000..4f52172d2 --- /dev/null +++ b/vendor/php-mcp/server/src/Defaults/BasicContainer.php @@ -0,0 +1,186 @@ + Cache for already created instances (shared singletons) */ + private array $instances = []; + + /** @var array Track classes currently being resolved to detect circular dependencies */ + private array $resolving = []; + + /** + * Finds an entry of the container by its identifier and returns it. + * + * @param string $id Identifier of the entry to look for (usually a FQCN). + * @return mixed Entry. + * + * @throws NotFoundExceptionInterface No entry was found for **this** identifier. + * @throws ContainerExceptionInterface Error while retrieving the entry (e.g., dependency resolution failure, circular dependency). + */ + public function get(string $id): mixed + { + // 1. Check instance cache + if (isset($this->instances[$id])) { + return $this->instances[$id]; + } + + // 2. Check if class exists + if (! class_exists($id) && ! interface_exists($id)) { // Also check interface for bindings + throw new NotFoundException("Class, interface, or entry '{$id}' not found."); + } + + // 7. Circular Dependency Check + if (isset($this->resolving[$id])) { + throw new ContainerException("Circular dependency detected while resolving '{$id}'. Resolution path: ".implode(' -> ', array_keys($this->resolving))." -> {$id}"); + } + + $this->resolving[$id] = true; // Mark as currently resolving + + try { + // 3. Reflect on the class + $reflector = new ReflectionClass($id); + + // Check if class is instantiable (abstract classes, interfaces cannot be directly instantiated) + if (! $reflector->isInstantiable()) { + // We might have an interface bound to a concrete class via set() + // This check is slightly redundant due to class_exists but good practice + throw new ContainerException("Class '{$id}' is not instantiable (e.g., abstract class or interface without explicit binding)."); + } + + // 4. Get the constructor + $constructor = $reflector->getConstructor(); + + // 5. If no constructor or constructor has no parameters, instantiate directly + if ($constructor === null || $constructor->getNumberOfParameters() === 0) { + $instance = $reflector->newInstance(); + } else { + // 6. Constructor has parameters, attempt to resolve them + $parameters = $constructor->getParameters(); + $resolvedArgs = []; + + foreach ($parameters as $parameter) { + $resolvedArgs[] = $this->resolveParameter($parameter, $id); + } + + // Instantiate with resolved arguments + $instance = $reflector->newInstanceArgs($resolvedArgs); + } + + // Cache the instance + $this->instances[$id] = $instance; + + return $instance; + + } catch (ReflectionException $e) { + throw new ContainerException("Reflection failed for '{$id}'.", 0, $e); + } catch (ContainerExceptionInterface $e) { // Re-throw container exceptions directly + throw $e; + } catch (Throwable $e) { // Catch other instantiation errors + throw new ContainerException("Failed to instantiate or resolve dependencies for '{$id}': ".$e->getMessage(), (int) $e->getCode(), $e); + } finally { + // 7. Remove from resolving stack once done (success or failure) + unset($this->resolving[$id]); + } + } + + /** + * Attempts to resolve a single constructor parameter. + * + * @throws ContainerExceptionInterface If a required dependency cannot be resolved. + */ + private function resolveParameter(ReflectionParameter $parameter, string $consumerClassId): mixed + { + // Check for type hint + $type = $parameter->getType(); + + if ($type instanceof ReflectionNamedType && ! $type->isBuiltin()) { + // Type hint is a class or interface name + $typeName = $type->getName(); + try { + // Recursively get the dependency + return $this->get($typeName); + } catch (NotFoundExceptionInterface $e) { + // Dependency class not found, fail ONLY if required + if (! $parameter->isOptional() && ! $parameter->allowsNull()) { + throw new ContainerException("Unresolvable dependency '{$typeName}' required by '{$consumerClassId}' constructor parameter \${$parameter->getName()}.", 0, $e); + } + // If optional or nullable, proceed (will check allowsNull/Default below) + } catch (ContainerExceptionInterface $e) { + // Dependency itself failed to resolve (e.g., its own deps, circular) + throw new ContainerException("Failed to resolve dependency '{$typeName}' for '{$consumerClassId}' parameter \${$parameter->getName()}: ".$e->getMessage(), 0, $e); + } + } + + // Check if parameter has a default value + if ($parameter->isDefaultValueAvailable()) { + return $parameter->getDefaultValue(); + } + + // Check if parameter allows null (and wasn't resolved above) + if ($parameter->allowsNull()) { + return null; + } + + // Check if it was a built-in type without a default (unresolvable by this basic container) + if ($type instanceof ReflectionNamedType && $type->isBuiltin()) { + throw new ContainerException("Cannot auto-wire built-in type '{$type->getName()}' for required parameter \${$parameter->getName()} in '{$consumerClassId}' constructor. Provide a default value or use a more advanced container."); + } + + // Check if it was a union/intersection type without a default (also unresolvable) + if ($type !== null && ! $type instanceof ReflectionNamedType) { + throw new ContainerException("Cannot auto-wire complex type (union/intersection) for required parameter \${$parameter->getName()} in '{$consumerClassId}' constructor. Provide a default value or use a more advanced container."); + } + + // If we reach here, it's an untyped, required parameter without a default. + // Or potentially an unresolvable optional class dependency where null is not allowed (edge case). + throw new ContainerException("Cannot resolve required parameter \${$parameter->getName()} for '{$consumerClassId}' constructor (untyped or unresolvable complex type)."); + } + + /** + * Returns true if the container can return an entry for the given identifier. + * Checks explicitly set instances and if the class/interface exists. + * Does not guarantee `get()` will succeed if auto-wiring fails. + */ + public function has(string $id): bool + { + return isset($this->instances[$id]) || class_exists($id) || interface_exists($id); + } + + /** + * Adds a pre-built instance or a factory/binding to the container. + * This basic version only supports pre-built instances (singletons). + */ + public function set(string $id, object $instance): void + { + // Could add support for closures/factories later if needed + $this->instances[$id] = $instance; + } +} + +// Keep custom exception classes as they are PSR-11 compliant placeholders +class ContainerException extends \Exception implements ContainerExceptionInterface +{ +} +class NotFoundException extends \Exception implements NotFoundExceptionInterface +{ +} diff --git a/vendor/php-mcp/server/src/Defaults/DefaultUuidSessionIdGenerator.php b/vendor/php-mcp/server/src/Defaults/DefaultUuidSessionIdGenerator.php new file mode 100644 index 000000000..52c0621ef --- /dev/null +++ b/vendor/php-mcp/server/src/Defaults/DefaultUuidSessionIdGenerator.php @@ -0,0 +1,20 @@ +values = array_map( + fn($case) => isset($case->value) && is_string($case->value) ? $case->value : $case->name, + $enumClass::cases() + ); + } + + public function getCompletions(string $currentValue, SessionInterface $session): array + { + if (empty($currentValue)) { + return $this->values; + } + + return array_values(array_filter( + $this->values, + fn(string $value) => str_starts_with($value, $currentValue) + )); + } +} diff --git a/vendor/php-mcp/server/src/Defaults/FileCache.php b/vendor/php-mcp/server/src/Defaults/FileCache.php new file mode 100644 index 000000000..e516e3755 --- /dev/null +++ b/vendor/php-mcp/server/src/Defaults/FileCache.php @@ -0,0 +1,327 @@ +ensureDirectoryExists(dirname($this->cacheFile)); + } + + // --------------------------------------------------------------------- + // PSR-16 Methods + // --------------------------------------------------------------------- + + public function get(string $key, mixed $default = null): mixed + { + $data = $this->readCacheFile(); + $key = $this->sanitizeKey($key); + + if (! isset($data[$key])) { + return $default; + } + + if ($this->isExpired($data[$key]['expiry'])) { + $this->delete($key); // Clean up expired entry + + return $default; + } + + return $data[$key]['value'] ?? $default; + } + + public function set(string $key, mixed $value, DateInterval|int|null $ttl = null): bool + { + $data = $this->readCacheFile(); + $key = $this->sanitizeKey($key); + + $data[$key] = [ + 'value' => $value, + 'expiry' => $this->calculateExpiry($ttl), + ]; + + return $this->writeCacheFile($data); + } + + public function delete(string $key): bool + { + $data = $this->readCacheFile(); + $key = $this->sanitizeKey($key); + + if (isset($data[$key])) { + unset($data[$key]); + + return $this->writeCacheFile($data); + } + + return true; // Key didn't exist, considered successful delete + } + + public function clear(): bool + { + // Write an empty array to the file + return $this->writeCacheFile([]); + } + + public function getMultiple(iterable $keys, mixed $default = null): iterable + { + $keys = $this->iterableToArray($keys); + $this->validateKeys($keys); + + $data = $this->readCacheFile(); + $results = []; + $needsWrite = false; + + foreach ($keys as $key) { + $sanitizedKey = $this->sanitizeKey($key); + if (! isset($data[$sanitizedKey])) { + $results[$key] = $default; + + continue; + } + + if ($this->isExpired($data[$sanitizedKey]['expiry'])) { + unset($data[$sanitizedKey]); // Clean up expired entry + $needsWrite = true; + $results[$key] = $default; + + continue; + } + + $results[$key] = $data[$sanitizedKey]['value'] ?? $default; + } + + if ($needsWrite) { + $this->writeCacheFile($data); + } + + return $results; + } + + public function setMultiple(iterable $values, DateInterval|int|null $ttl = null): bool + { + $values = $this->iterableToArray($values); + $this->validateKeys(array_keys($values)); + + $data = $this->readCacheFile(); + $expiry = $this->calculateExpiry($ttl); + + foreach ($values as $key => $value) { + $sanitizedKey = $this->sanitizeKey((string) $key); + $data[$sanitizedKey] = [ + 'value' => $value, + 'expiry' => $expiry, + ]; + } + + return $this->writeCacheFile($data); + } + + public function deleteMultiple(iterable $keys): bool + { + $keys = $this->iterableToArray($keys); + $this->validateKeys($keys); + + $data = $this->readCacheFile(); + $deleted = false; + + foreach ($keys as $key) { + $sanitizedKey = $this->sanitizeKey($key); + if (isset($data[$sanitizedKey])) { + unset($data[$sanitizedKey]); + $deleted = true; + } + } + + if ($deleted) { + return $this->writeCacheFile($data); + } + + return true; // No keys existed or no changes made + } + + public function has(string $key): bool + { + $data = $this->readCacheFile(); + $key = $this->sanitizeKey($key); + + if (! isset($data[$key])) { + return false; + } + + if ($this->isExpired($data[$key]['expiry'])) { + $this->delete($key); // Clean up expired + + return false; + } + + return true; + } + + // --------------------------------------------------------------------- + // Internal Methods + // --------------------------------------------------------------------- + + private function readCacheFile(): array + { + if (! file_exists($this->cacheFile) || filesize($this->cacheFile) === 0) { + return []; + } + + $handle = @fopen($this->cacheFile, 'rb'); + if ($handle === false) { + return []; + } + + try { + if (! flock($handle, LOCK_SH)) { + return []; + } + $content = stream_get_contents($handle); + flock($handle, LOCK_UN); + + if ($content === false || $content === '') { + return []; + } + + $data = unserialize($content); + if ($data === false) { + return []; + } + + return $data; + } finally { + if (is_resource($handle)) { + fclose($handle); + } + } + } + + private function writeCacheFile(array $data): bool + { + $jsonData = serialize($data); + + if ($jsonData === false) { + return false; + } + + $handle = @fopen($this->cacheFile, 'cb'); + if ($handle === false) { + return false; + } + + try { + if (! flock($handle, LOCK_EX)) { + return false; + } + if (! ftruncate($handle, 0)) { + return false; + } + if (fwrite($handle, $jsonData) === false) { + return false; + } + fflush($handle); + flock($handle, LOCK_UN); + @chmod($this->cacheFile, $this->filePermission); + + return true; + } catch (Throwable $e) { + flock($handle, LOCK_UN); // Ensure lock release on error + + return false; + } finally { + if (is_resource($handle)) { + fclose($handle); + } + } + } + + private function ensureDirectoryExists(string $directory): void + { + if (! is_dir($directory)) { + if (! @mkdir($directory, $this->dirPermission, true)) { + throw new InvalidArgumentException("Cache directory does not exist and could not be created: {$directory}"); + } + @chmod($directory, $this->dirPermission); + } + } + + private function calculateExpiry(DateInterval|int|null $ttl): ?int + { + if ($ttl === null) { + return null; + } + $now = time(); + if (is_int($ttl)) { + return $ttl <= 0 ? $now - 1 : $now + $ttl; + } + if ($ttl instanceof DateInterval) { + try { + return (new DateTimeImmutable())->add($ttl)->getTimestamp(); + } catch (Throwable $e) { + return null; + } + } + throw new InvalidArgumentException('Invalid TTL type provided. Must be null, int, or DateInterval.'); + } + + private function isExpired(?int $expiry): bool + { + return $expiry !== null && time() >= $expiry; + } + + private function sanitizeKey(string $key): string + { + if ($key === '') { + throw new InvalidArgumentException('Cache key cannot be empty.'); + } + + // PSR-16 validation (optional stricter check) + // if (preg_match('/[{}()\/@:]/', $key)) { + // throw new InvalidArgumentException("Cache key \"{$key}\" contains reserved characters."); + // } + return $key; + } + + private function validateKeys(array $keys): void + { + foreach ($keys as $key) { + if (! is_string($key)) { + throw new InvalidArgumentException('Cache key must be a string, got ' . gettype($key)); + } + $this->sanitizeKey($key); + } + } + + private function iterableToArray(iterable $iterable): array + { + if (is_array($iterable)) { + return $iterable; + } + + return iterator_to_array($iterable); + } +} diff --git a/vendor/php-mcp/server/src/Defaults/InMemoryEventStore.php b/vendor/php-mcp/server/src/Defaults/InMemoryEventStore.php new file mode 100644 index 000000000..1c151ecd1 --- /dev/null +++ b/vendor/php-mcp/server/src/Defaults/InMemoryEventStore.php @@ -0,0 +1,78 @@ + + * Example: [eventId1 => ['streamId' => 'abc', 'message' => '...']] + */ + private array $events = []; + + private function generateEventId(string $streamId): string + { + return $streamId . '_' . (int)(microtime(true) * 1000) . '_' . bin2hex(random_bytes(4)); + } + + private function getStreamIdFromEventId(string $eventId): ?string + { + $parts = explode('_', $eventId); + return $parts[0] ?? null; + } + + public function storeEvent(string $streamId, string $message): string + { + $eventId = $this->generateEventId($streamId); + + $this->events[$eventId] = [ + 'streamId' => $streamId, + 'message' => $message, + ]; + + return $eventId; + } + + public function replayEventsAfter(string $lastEventId, callable $sendCallback): void + { + if (!isset($this->events[$lastEventId])) { + return; + } + + $streamId = $this->getStreamIdFromEventId($lastEventId); + if ($streamId === null) { + return; + } + + $foundLastEvent = false; + + // Sort by eventId for deterministic ordering + ksort($this->events); + + foreach ($this->events as $eventId => ['streamId' => $eventStreamId, 'message' => $message]) { + if ($eventStreamId !== $streamId) { + continue; + } + + if ($eventId === $lastEventId) { + $foundLastEvent = true; + continue; + } + + if ($foundLastEvent) { + $sendCallback($eventId, $message); + } + } + } +} diff --git a/vendor/php-mcp/server/src/Defaults/ListCompletionProvider.php b/vendor/php-mcp/server/src/Defaults/ListCompletionProvider.php new file mode 100644 index 000000000..1b1d3776a --- /dev/null +++ b/vendor/php-mcp/server/src/Defaults/ListCompletionProvider.php @@ -0,0 +1,25 @@ +values; + } + + return array_values(array_filter( + $this->values, + fn(string $value) => str_starts_with($value, $currentValue) + )); + } +} diff --git a/vendor/php-mcp/server/src/Defaults/SystemClock.php b/vendor/php-mcp/server/src/Defaults/SystemClock.php new file mode 100644 index 000000000..a18c4ceb1 --- /dev/null +++ b/vendor/php-mcp/server/src/Defaults/SystemClock.php @@ -0,0 +1,16 @@ +container = $this->configuration->container; + $this->logger = $this->configuration->logger; + + $this->schemaValidator ??= new SchemaValidator($this->logger); + } + + public function handleRequest(Request $request, SessionInterface $session): Result + { + switch ($request->method) { + case 'initialize': + $request = InitializeRequest::fromRequest($request); + return $this->handleInitialize($request, $session); + case 'ping': + $request = PingRequest::fromRequest($request); + return $this->handlePing($request); + case 'tools/list': + $request = ListToolsRequest::fromRequest($request); + return $this->handleToolList($request); + case 'tools/call': + $request = CallToolRequest::fromRequest($request); + return $this->handleToolCall($request); + case 'resources/list': + $request = ListResourcesRequest::fromRequest($request); + return $this->handleResourcesList($request); + case 'resources/templates/list': + $request = ListResourceTemplatesRequest::fromRequest($request); + return $this->handleResourceTemplateList($request); + case 'resources/read': + $request = ReadResourceRequest::fromRequest($request); + return $this->handleResourceRead($request); + case 'resources/subscribe': + $request = ResourceSubscribeRequest::fromRequest($request); + return $this->handleResourceSubscribe($request, $session); + case 'resources/unsubscribe': + $request = ResourceUnsubscribeRequest::fromRequest($request); + return $this->handleResourceUnsubscribe($request, $session); + case 'prompts/list': + $request = ListPromptsRequest::fromRequest($request); + return $this->handlePromptsList($request); + case 'prompts/get': + $request = GetPromptRequest::fromRequest($request); + return $this->handlePromptGet($request); + case 'logging/setLevel': + $request = SetLogLevelRequest::fromRequest($request); + return $this->handleLoggingSetLevel($request, $session); + case 'completion/complete': + $request = CompletionCompleteRequest::fromRequest($request); + return $this->handleCompletionComplete($request, $session); + default: + throw McpServerException::methodNotFound("Method '{$request->method}' not found."); + } + } + + public function handleNotification(Notification $notification, SessionInterface $session): void + { + switch ($notification->method) { + case 'notifications/initialized': + $notification = InitializedNotification::fromNotification($notification); + $this->handleNotificationInitialized($notification, $session); + } + } + + public function handleInitialize(InitializeRequest $request, SessionInterface $session): InitializeResult + { + if (in_array($request->protocolVersion, Protocol::SUPPORTED_PROTOCOL_VERSIONS)) { + $protocolVersion = $request->protocolVersion; + } else { + $protocolVersion = Protocol::LATEST_PROTOCOL_VERSION; + } + + $session->set('client_info', $request->clientInfo->toArray()); + $session->set('protocol_version', $protocolVersion); + + $serverInfo = $this->configuration->serverInfo; + $capabilities = $this->configuration->capabilities; + $instructions = $this->configuration->instructions; + + return new InitializeResult($protocolVersion, $capabilities, $serverInfo, $instructions); + } + + public function handlePing(PingRequest $request): EmptyResult + { + return new EmptyResult(); + } + + public function handleToolList(ListToolsRequest $request): ListToolsResult + { + $limit = $this->configuration->paginationLimit; + $offset = $this->decodeCursor($request->cursor); + $allItems = $this->registry->getTools(); + $pagedItems = array_slice($allItems, $offset, $limit); + $nextCursor = $this->encodeNextCursor($offset, count($pagedItems), count($allItems), $limit); + + return new ListToolsResult(array_values($pagedItems), $nextCursor); + } + + public function handleToolCall(CallToolRequest $request): CallToolResult + { + $toolName = $request->name; + $arguments = $request->arguments; + + $registeredTool = $this->registry->getTool($toolName); + if (! $registeredTool) { + throw McpServerException::methodNotFound("Tool '{$toolName}' not found."); + } + + $inputSchema = $registeredTool->schema->inputSchema; + + $validationErrors = $this->schemaValidator->validateAgainstJsonSchema($arguments, $inputSchema); + + if (! empty($validationErrors)) { + $errorMessages = []; + + foreach ($validationErrors as $errorDetail) { + $pointer = $errorDetail['pointer'] ?? ''; + $message = $errorDetail['message'] ?? 'Unknown validation error'; + $errorMessages[] = ($pointer !== '/' && $pointer !== '' ? "Property '{$pointer}': " : '') . $message; + } + + $summaryMessage = "Invalid parameters for tool '{$toolName}': " . implode('; ', array_slice($errorMessages, 0, 3)); + + if (count($errorMessages) > 3) { + $summaryMessage .= '; ...and more errors.'; + } + + throw McpServerException::invalidParams($summaryMessage, data: ['validation_errors' => $validationErrors]); + } + + try { + $result = $registeredTool->call($this->container, $arguments); + + return new CallToolResult($result, false); + } catch (JsonException $e) { + $this->logger->warning('Failed to JSON encode tool result.', ['tool' => $toolName, 'exception' => $e]); + $errorMessage = "Failed to serialize tool result: {$e->getMessage()}"; + + return new CallToolResult([new TextContent($errorMessage)], true); + } catch (Throwable $toolError) { + $this->logger->error('Tool execution failed.', ['tool' => $toolName, 'exception' => $toolError]); + $errorMessage = "Tool execution failed: {$toolError->getMessage()}"; + + return new CallToolResult([new TextContent($errorMessage)], true); + } + } + + public function handleResourcesList(ListResourcesRequest $request): ListResourcesResult + { + $limit = $this->configuration->paginationLimit; + $offset = $this->decodeCursor($request->cursor); + $allItems = $this->registry->getResources(); + $pagedItems = array_slice($allItems, $offset, $limit); + $nextCursor = $this->encodeNextCursor($offset, count($pagedItems), count($allItems), $limit); + + return new ListResourcesResult(array_values($pagedItems), $nextCursor); + } + + public function handleResourceTemplateList(ListResourceTemplatesRequest $request): ListResourceTemplatesResult + { + $limit = $this->configuration->paginationLimit; + $offset = $this->decodeCursor($request->cursor); + $allItems = $this->registry->getResourceTemplates(); + $pagedItems = array_slice($allItems, $offset, $limit); + $nextCursor = $this->encodeNextCursor($offset, count($pagedItems), count($allItems), $limit); + + return new ListResourceTemplatesResult(array_values($pagedItems), $nextCursor); + } + + public function handleResourceRead(ReadResourceRequest $request): ReadResourceResult + { + $uri = $request->uri; + + $registeredResource = $this->registry->getResource($uri); + + if (! $registeredResource) { + throw McpServerException::invalidParams("Resource URI '{$uri}' not found."); + } + + try { + $result = $registeredResource->read($this->container, $uri); + + return new ReadResourceResult($result); + } catch (JsonException $e) { + $this->logger->warning('Failed to JSON encode resource content.', ['exception' => $e, 'uri' => $uri]); + throw McpServerException::internalError("Failed to serialize resource content for '{$uri}'.", $e); + } catch (McpServerException $e) { + throw $e; + } catch (Throwable $e) { + $this->logger->error('Resource read failed.', ['uri' => $uri, 'exception' => $e]); + throw McpServerException::resourceReadFailed($uri, $e); + } + } + + public function handleResourceSubscribe(ResourceSubscribeRequest $request, SessionInterface $session): EmptyResult + { + $this->subscriptionManager->subscribe($session->getId(), $request->uri); + return new EmptyResult(); + } + + public function handleResourceUnsubscribe(ResourceUnsubscribeRequest $request, SessionInterface $session): EmptyResult + { + $this->subscriptionManager->unsubscribe($session->getId(), $request->uri); + return new EmptyResult(); + } + + public function handlePromptsList(ListPromptsRequest $request): ListPromptsResult + { + $limit = $this->configuration->paginationLimit; + $offset = $this->decodeCursor($request->cursor); + $allItems = $this->registry->getPrompts(); + $pagedItems = array_slice($allItems, $offset, $limit); + $nextCursor = $this->encodeNextCursor($offset, count($pagedItems), count($allItems), $limit); + + return new ListPromptsResult(array_values($pagedItems), $nextCursor); + } + + public function handlePromptGet(GetPromptRequest $request): GetPromptResult + { + $promptName = $request->name; + $arguments = $request->arguments; + + $registeredPrompt = $this->registry->getPrompt($promptName); + if (! $registeredPrompt) { + throw McpServerException::invalidParams("Prompt '{$promptName}' not found."); + } + + $arguments = (array) $arguments; + + foreach ($registeredPrompt->schema->arguments as $argDef) { + if ($argDef->required && ! array_key_exists($argDef->name, $arguments)) { + throw McpServerException::invalidParams("Missing required argument '{$argDef->name}' for prompt '{$promptName}'."); + } + } + + try { + $result = $registeredPrompt->get($this->container, $arguments); + + return new GetPromptResult($result, $registeredPrompt->schema->description); + } catch (JsonException $e) { + $this->logger->warning('Failed to JSON encode prompt messages.', ['exception' => $e, 'promptName' => $promptName]); + throw McpServerException::internalError("Failed to serialize prompt messages for '{$promptName}'.", $e); + } catch (McpServerException $e) { + throw $e; + } catch (Throwable $e) { + $this->logger->error('Prompt generation failed.', ['promptName' => $promptName, 'exception' => $e]); + throw McpServerException::promptGenerationFailed($promptName, $e); + } + } + + public function handleLoggingSetLevel(SetLogLevelRequest $request, SessionInterface $session): EmptyResult + { + $level = $request->level; + + $session->set('log_level', $level->value); + + $this->logger->info("Log level set to '{$level->value}'.", ['sessionId' => $session->getId()]); + + return new EmptyResult(); + } + + public function handleCompletionComplete(CompletionCompleteRequest $request, SessionInterface $session): CompletionCompleteResult + { + $ref = $request->ref; + $argumentName = $request->argument['name']; + $currentValue = $request->argument['value']; + + $identifier = null; + + if ($ref->type === 'ref/prompt') { + $identifier = $ref->name; + $registeredPrompt = $this->registry->getPrompt($identifier); + if (! $registeredPrompt) { + throw McpServerException::invalidParams("Prompt '{$identifier}' not found."); + } + + $foundArg = false; + foreach ($registeredPrompt->schema->arguments as $arg) { + if ($arg->name === $argumentName) { + $foundArg = true; + break; + } + } + if (! $foundArg) { + throw McpServerException::invalidParams("Argument '{$argumentName}' not found in prompt '{$identifier}'."); + } + + return $registeredPrompt->complete($this->container, $argumentName, $currentValue, $session); + } elseif ($ref->type === 'ref/resource') { + $identifier = $ref->uri; + $registeredResourceTemplate = $this->registry->getResourceTemplate($identifier); + if (! $registeredResourceTemplate) { + throw McpServerException::invalidParams("Resource template '{$identifier}' not found."); + } + + $foundArg = false; + foreach ($registeredResourceTemplate->getVariableNames() as $uriVariableName) { + if ($uriVariableName === $argumentName) { + $foundArg = true; + break; + } + } + + if (! $foundArg) { + throw McpServerException::invalidParams("URI variable '{$argumentName}' not found in resource template '{$identifier}'."); + } + + return $registeredResourceTemplate->complete($this->container, $argumentName, $currentValue, $session); + } else { + throw McpServerException::invalidParams("Invalid ref type '{$ref->type}' for completion complete request."); + } + } + + public function handleNotificationInitialized(InitializedNotification $notification, SessionInterface $session): EmptyResult + { + $session->set('initialized', true); + + return new EmptyResult(); + } + + private function decodeCursor(?string $cursor): int + { + if ($cursor === null) { + return 0; + } + + $decoded = base64_decode($cursor, true); + if ($decoded === false) { + $this->logger->warning('Received invalid pagination cursor (not base64)', ['cursor' => $cursor]); + + return 0; + } + + if (preg_match('/^offset=(\d+)$/', $decoded, $matches)) { + return (int) $matches[1]; + } + + $this->logger->warning('Received invalid pagination cursor format', ['cursor' => $decoded]); + + return 0; + } + + private function encodeNextCursor(int $currentOffset, int $returnedCount, int $totalCount, int $limit): ?string + { + $nextOffset = $currentOffset + $returnedCount; + if ($returnedCount > 0 && $nextOffset < $totalCount) { + return base64_encode("offset={$nextOffset}"); + } + + return null; + } +} diff --git a/vendor/php-mcp/server/src/Elements/RegisteredElement.php b/vendor/php-mcp/server/src/Elements/RegisteredElement.php new file mode 100644 index 000000000..a409b4980 --- /dev/null +++ b/vendor/php-mcp/server/src/Elements/RegisteredElement.php @@ -0,0 +1,268 @@ +handler = $handler; + $this->isManual = $isManual; + } + + public function handle(ContainerInterface $container, array $arguments): mixed + { + if (is_string($this->handler)) { + if (class_exists($this->handler) && method_exists($this->handler, '__invoke')) { + $reflection = new \ReflectionMethod($this->handler, '__invoke'); + $arguments = $this->prepareArguments($reflection, $arguments); + $instance = $container->get($this->handler); + return call_user_func($instance, ...$arguments); + } + + if (function_exists($this->handler)) { + $reflection = new \ReflectionFunction($this->handler); + $arguments = $this->prepareArguments($reflection, $arguments); + return call_user_func($this->handler, ...$arguments); + } + } + + if (is_callable($this->handler)) { + $reflection = $this->getReflectionForCallable($this->handler); + $arguments = $this->prepareArguments($reflection, $arguments); + return call_user_func($this->handler, ...$arguments); + } + + if (is_array($this->handler)) { + [$className, $methodName] = $this->handler; + $reflection = new \ReflectionMethod($className, $methodName); + $arguments = $this->prepareArguments($reflection, $arguments); + + $instance = $container->get($className); + return call_user_func([$instance, $methodName], ...$arguments); + } + + throw new \InvalidArgumentException('Invalid handler type'); + } + + + protected function prepareArguments(\ReflectionFunctionAbstract $reflection, array $arguments): array + { + $finalArgs = []; + + foreach ($reflection->getParameters() as $parameter) { + // TODO: Handle variadic parameters. + $paramName = $parameter->getName(); + $paramPosition = $parameter->getPosition(); + + if (isset($arguments[$paramName])) { + $argument = $arguments[$paramName]; + try { + $finalArgs[$paramPosition] = $this->castArgumentType($argument, $parameter); + } catch (InvalidArgumentException $e) { + throw McpServerException::invalidParams($e->getMessage(), $e); + } catch (Throwable $e) { + throw McpServerException::internalError( + "Error processing parameter `{$paramName}`: {$e->getMessage()}", + $e + ); + } + } elseif ($parameter->isDefaultValueAvailable()) { + $finalArgs[$paramPosition] = $parameter->getDefaultValue(); + } elseif ($parameter->allowsNull()) { + $finalArgs[$paramPosition] = null; + } elseif ($parameter->isOptional()) { + continue; + } else { + $reflectionName = $reflection instanceof \ReflectionMethod + ? $reflection->class . '::' . $reflection->name + : 'Closure'; + throw McpServerException::internalError( + "Missing required argument `{$paramName}` for {$reflectionName}." + ); + } + } + + return array_values($finalArgs); + } + + /** + * Gets a ReflectionMethod or ReflectionFunction for a callable. + */ + private function getReflectionForCallable(callable $handler): \ReflectionMethod|\ReflectionFunction + { + if (is_string($handler)) { + return new \ReflectionFunction($handler); + } + + if ($handler instanceof \Closure) { + return new \ReflectionFunction($handler); + } + + if (is_array($handler) && count($handler) === 2) { + [$class, $method] = $handler; + return new \ReflectionMethod($class, $method); + } + + throw new \InvalidArgumentException('Cannot create reflection for this callable type'); + } + + /** + * Attempts type casting based on ReflectionParameter type hints. + * + * @throws InvalidArgumentException If casting is impossible for the required type. + * @throws TypeError If internal PHP casting fails unexpectedly. + */ + private function castArgumentType(mixed $argument, ReflectionParameter $parameter): mixed + { + $type = $parameter->getType(); + + if ($argument === null) { + if ($type && $type->allowsNull()) { + return null; + } + } + + if (! $type instanceof ReflectionNamedType) { + return $argument; + } + + $typeName = $type->getName(); + + if (enum_exists($typeName)) { + if (is_object($argument) && $argument instanceof $typeName) { + return $argument; + } + + if (is_subclass_of($typeName, \BackedEnum::class)) { + $value = $typeName::tryFrom($argument); + if ($value === null) { + throw new InvalidArgumentException( + "Invalid value '{$argument}' for backed enum {$typeName}. Expected one of its backing values.", + ); + } + return $value; + } else { + if (is_string($argument)) { + foreach ($typeName::cases() as $case) { + if ($case->name === $argument) { + return $case; + } + } + $validNames = array_map(fn($c) => $c->name, $typeName::cases()); + throw new InvalidArgumentException( + "Invalid value '{$argument}' for unit enum {$typeName}. Expected one of: " . implode(', ', $validNames) . "." + ); + } else { + throw new InvalidArgumentException( + "Invalid value type '{$argument}' for unit enum {$typeName}. Expected a string matching a case name." + ); + } + } + } + + try { + return match (strtolower($typeName)) { + 'int', 'integer' => $this->castToInt($argument), + 'string' => (string) $argument, + 'bool', 'boolean' => $this->castToBoolean($argument), + 'float', 'double' => $this->castToFloat($argument), + 'array' => $this->castToArray($argument), + default => $argument, + }; + } catch (TypeError $e) { + throw new InvalidArgumentException( + "Value cannot be cast to required type `{$typeName}`.", + 0, + $e + ); + } + } + + /** Helper to cast strictly to boolean */ + private function castToBoolean(mixed $argument): bool + { + if (is_bool($argument)) { + return $argument; + } + if ($argument === 1 || $argument === '1' || strtolower((string) $argument) === 'true') { + return true; + } + if ($argument === 0 || $argument === '0' || strtolower((string) $argument) === 'false') { + return false; + } + throw new InvalidArgumentException('Cannot cast value to boolean. Use true/false/1/0.'); + } + + /** Helper to cast strictly to integer */ + private function castToInt(mixed $argument): int + { + if (is_int($argument)) { + return $argument; + } + if (is_numeric($argument) && floor((float) $argument) == $argument && ! is_string($argument)) { + return (int) $argument; + } + if (is_string($argument) && ctype_digit(ltrim($argument, '-'))) { + return (int) $argument; + } + throw new InvalidArgumentException('Cannot cast value to integer. Expected integer representation.'); + } + + /** Helper to cast strictly to float */ + private function castToFloat(mixed $argument): float + { + if (is_float($argument)) { + return $argument; + } + if (is_int($argument)) { + return (float) $argument; + } + if (is_numeric($argument)) { + return (float) $argument; + } + throw new InvalidArgumentException('Cannot cast value to float. Expected numeric representation.'); + } + + /** Helper to cast strictly to array */ + private function castToArray(mixed $argument): array + { + if (is_array($argument)) { + return $argument; + } + throw new InvalidArgumentException('Cannot cast value to array. Expected array.'); + } + + public function toArray(): array + { + return [ + 'handler' => $this->handler, + 'isManual' => $this->isManual, + ]; + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } +} diff --git a/vendor/php-mcp/server/src/Elements/RegisteredPrompt.php b/vendor/php-mcp/server/src/Elements/RegisteredPrompt.php new file mode 100644 index 000000000..3ebcc0dab --- /dev/null +++ b/vendor/php-mcp/server/src/Elements/RegisteredPrompt.php @@ -0,0 +1,330 @@ +handle($container, $arguments); + + return $this->formatResult($result); + } + + public function complete(ContainerInterface $container, string $argument, string $value, SessionInterface $session): CompletionCompleteResult + { + $providerClassOrInstance = $this->completionProviders[$argument] ?? null; + if ($providerClassOrInstance === null) { + return new CompletionCompleteResult([]); + } + + if (is_string($providerClassOrInstance)) { + if (! class_exists($providerClassOrInstance)) { + throw new \RuntimeException("Completion provider class '{$providerClassOrInstance}' does not exist."); + } + + $provider = $container->get($providerClassOrInstance); + } else { + $provider = $providerClassOrInstance; + } + + $completions = $provider->getCompletions($value, $session); + + $total = count($completions); + $hasMore = $total > 100; + + $pagedCompletions = array_slice($completions, 0, 100); + + return new CompletionCompleteResult($pagedCompletions, $total, $hasMore); + } + + /** + * Formats the raw result of a prompt generator into an array of MCP PromptMessages. + * + * @param mixed $promptGenerationResult Expected: array of message structures. + * @return PromptMessage[] Array of PromptMessage objects. + * + * @throws \RuntimeException If the result cannot be formatted. + * @throws \JsonException If JSON encoding fails. + */ + protected function formatResult(mixed $promptGenerationResult): array + { + if ($promptGenerationResult instanceof PromptMessage) { + return [$promptGenerationResult]; + } + + if (! is_array($promptGenerationResult)) { + throw new \RuntimeException('Prompt generator method must return an array of messages.'); + } + + if (empty($promptGenerationResult)) { + return []; + } + + if (is_array($promptGenerationResult)) { + $allArePromptMessages = true; + $hasPromptMessages = false; + + foreach ($promptGenerationResult as $item) { + if ($item instanceof PromptMessage) { + $hasPromptMessages = true; + } else { + $allArePromptMessages = false; + } + } + + if ($allArePromptMessages && $hasPromptMessages) { + return $promptGenerationResult; + } + + if ($hasPromptMessages) { + $result = []; + foreach ($promptGenerationResult as $index => $item) { + if ($item instanceof PromptMessage) { + $result[] = $item; + } else { + $result = array_merge($result, $this->formatResult($item)); + } + } + return $result; + } + + if (! array_is_list($promptGenerationResult)) { + if (isset($promptGenerationResult['user']) || isset($promptGenerationResult['assistant'])) { + $result = []; + if (isset($promptGenerationResult['user'])) { + $userContent = $this->formatContent($promptGenerationResult['user']); + $result[] = PromptMessage::make(Role::User, $userContent); + } + if (isset($promptGenerationResult['assistant'])) { + $assistantContent = $this->formatContent($promptGenerationResult['assistant']); + $result[] = PromptMessage::make(Role::Assistant, $assistantContent); + } + return $result; + } + + if (isset($promptGenerationResult['role']) && isset($promptGenerationResult['content'])) { + return [$this->formatMessage($promptGenerationResult)]; + } + + throw new \RuntimeException('Associative array must contain either role/content keys or user/assistant keys.'); + } + + $formattedMessages = []; + foreach ($promptGenerationResult as $index => $message) { + if ($message instanceof PromptMessage) { + $formattedMessages[] = $message; + } else { + $formattedMessages[] = $this->formatMessage($message, $index); + } + } + return $formattedMessages; + } + + throw new \RuntimeException('Invalid prompt generation result format.'); + } + + /** + * Formats a single message into a PromptMessage. + */ + private function formatMessage(mixed $message, ?int $index = null): PromptMessage + { + $indexStr = $index !== null ? " at index {$index}" : ''; + + if (! is_array($message) || ! array_key_exists('role', $message) || ! array_key_exists('content', $message)) { + throw new \RuntimeException("Invalid message format{$indexStr}. Expected an array with 'role' and 'content' keys."); + } + + $role = $message['role'] instanceof Role ? $message['role'] : Role::tryFrom($message['role']); + if ($role === null) { + throw new \RuntimeException("Invalid role '{$message['role']}' in prompt message{$indexStr}. Only 'user' or 'assistant' are supported."); + } + + $content = $this->formatContent($message['content'], $index); + + return new PromptMessage($role, $content); + } + + /** + * Formats content into a proper Content object. + */ + private function formatContent(mixed $content, ?int $index = null): TextContent|ImageContent|AudioContent|EmbeddedResource + { + $indexStr = $index !== null ? " at index {$index}" : ''; + + if ($content instanceof Content) { + if ( + $content instanceof TextContent || $content instanceof ImageContent || + $content instanceof AudioContent || $content instanceof EmbeddedResource + ) { + return $content; + } + throw new \RuntimeException("Invalid Content type{$indexStr}. PromptMessage only supports TextContent, ImageContent, AudioContent, or EmbeddedResource."); + } + + if (is_string($content)) { + return TextContent::make($content); + } + + if (is_array($content) && isset($content['type'])) { + return $this->formatTypedContent($content, $index); + } + + if (is_scalar($content) || $content === null) { + $stringContent = $content === null ? '(null)' : (is_bool($content) ? ($content ? 'true' : 'false') : (string)$content); + return TextContent::make($stringContent); + } + + $jsonContent = json_encode($content, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); + return TextContent::make($jsonContent); + } + + /** + * Formats typed content arrays into Content objects. + */ + private function formatTypedContent(array $content, ?int $index = null): TextContent|ImageContent|AudioContent|EmbeddedResource + { + $indexStr = $index !== null ? " at index {$index}" : ''; + $type = $content['type']; + + return match ($type) { + 'text' => $this->formatTextContent($content, $indexStr), + 'image' => $this->formatImageContent($content, $indexStr), + 'audio' => $this->formatAudioContent($content, $indexStr), + 'resource' => $this->formatResourceContent($content, $indexStr), + default => throw new \RuntimeException("Invalid content type '{$type}'{$indexStr}.") + }; + } + + private function formatTextContent(array $content, string $indexStr): TextContent + { + if (! isset($content['text']) || ! is_string($content['text'])) { + throw new \RuntimeException("Invalid 'text' content{$indexStr}: Missing or invalid 'text' string."); + } + return TextContent::make($content['text']); + } + + private function formatImageContent(array $content, string $indexStr): ImageContent + { + if (! isset($content['data']) || ! is_string($content['data'])) { + throw new \RuntimeException("Invalid 'image' content{$indexStr}: Missing or invalid 'data' string (base64)."); + } + if (! isset($content['mimeType']) || ! is_string($content['mimeType'])) { + throw new \RuntimeException("Invalid 'image' content{$indexStr}: Missing or invalid 'mimeType' string."); + } + return ImageContent::make($content['data'], $content['mimeType']); + } + + private function formatAudioContent(array $content, string $indexStr): AudioContent + { + if (! isset($content['data']) || ! is_string($content['data'])) { + throw new \RuntimeException("Invalid 'audio' content{$indexStr}: Missing or invalid 'data' string (base64)."); + } + if (! isset($content['mimeType']) || ! is_string($content['mimeType'])) { + throw new \RuntimeException("Invalid 'audio' content{$indexStr}: Missing or invalid 'mimeType' string."); + } + return AudioContent::make($content['data'], $content['mimeType']); + } + + private function formatResourceContent(array $content, string $indexStr): EmbeddedResource + { + if (! isset($content['resource']) || ! is_array($content['resource'])) { + throw new \RuntimeException("Invalid 'resource' content{$indexStr}: Missing or invalid 'resource' object."); + } + + $resource = $content['resource']; + if (! isset($resource['uri']) || ! is_string($resource['uri'])) { + throw new \RuntimeException("Invalid resource{$indexStr}: Missing or invalid 'uri'."); + } + + if (isset($resource['text']) && is_string($resource['text'])) { + $resourceObj = TextResourceContents::make($resource['uri'], $resource['mimeType'] ?? 'text/plain', $resource['text']); + } elseif (isset($resource['blob']) && is_string($resource['blob'])) { + $resourceObj = BlobResourceContents::make( + $resource['uri'], + $resource['mimeType'] ?? 'application/octet-stream', + $resource['blob'] + ); + } else { + throw new \RuntimeException("Invalid resource{$indexStr}: Must contain 'text' or 'blob'."); + } + + return new EmbeddedResource($resourceObj); + } + + public function toArray(): array + { + $completionProviders = []; + foreach ($this->completionProviders as $argument => $provider) { + $completionProviders[$argument] = serialize($provider); + } + + return [ + 'schema' => $this->schema->toArray(), + 'completionProviders' => $completionProviders, + ...parent::toArray(), + ]; + } + + public static function fromArray(array $data): self|false + { + try { + if (! isset($data['schema']) || ! isset($data['handler'])) { + return false; + } + + $completionProviders = []; + foreach ($data['completionProviders'] ?? [] as $argument => $provider) { + $completionProviders[$argument] = unserialize($provider); + } + + return new self( + Prompt::fromArray($data['schema']), + $data['handler'], + $data['isManual'] ?? false, + $completionProviders, + ); + } catch (Throwable $e) { + return false; + } + } +} diff --git a/vendor/php-mcp/server/src/Elements/RegisteredResource.php b/vendor/php-mcp/server/src/Elements/RegisteredResource.php new file mode 100644 index 000000000..489aa2462 --- /dev/null +++ b/vendor/php-mcp/server/src/Elements/RegisteredResource.php @@ -0,0 +1,233 @@ + Array of ResourceContents objects. + */ + public function read(ContainerInterface $container, string $uri): array + { + $result = $this->handle($container, ['uri' => $uri]); + + return $this->formatResult($result, $uri, $this->schema->mimeType); + } + + /** + * Formats the raw result of a resource read operation into MCP ResourceContent items. + * + * @param mixed $readResult The raw result from the resource handler method. + * @param string $uri The URI of the resource that was read. + * @param ?string $mimeType The MIME type from the ResourceDefinition. + * @return array Array of ResourceContents objects. + * + * @throws \RuntimeException If the result cannot be formatted. + * + * Supported result types: + * - ResourceContent: Used as-is + * - EmbeddedResource: Resource is extracted from the EmbeddedResource + * - string: Converted to text content with guessed or provided MIME type + * - stream resource: Read and converted to blob with provided MIME type + * - array with 'blob' key: Used as blob content + * - array with 'text' key: Used as text content + * - SplFileInfo: Read and converted to blob + * - array: Converted to JSON if MIME type is application/json or contains 'json' + * For other MIME types, will try to convert to JSON with a warning + */ + protected function formatResult(mixed $readResult, string $uri, ?string $mimeType): array + { + if ($readResult instanceof ResourceContents) { + return [$readResult]; + } + + if ($readResult instanceof EmbeddedResource) { + return [$readResult->resource]; + } + + if (is_array($readResult)) { + if (empty($readResult)) { + return [TextResourceContents::make($uri, 'application/json', '[]')]; + } + + $allAreResourceContents = true; + $hasResourceContents = false; + $allAreEmbeddedResource = true; + $hasEmbeddedResource = false; + + foreach ($readResult as $item) { + if ($item instanceof ResourceContents) { + $hasResourceContents = true; + $allAreEmbeddedResource = false; + } elseif ($item instanceof EmbeddedResource) { + $hasEmbeddedResource = true; + $allAreResourceContents = false; + } else { + $allAreResourceContents = false; + $allAreEmbeddedResource = false; + } + } + + if ($allAreResourceContents && $hasResourceContents) { + return $readResult; + } + + if ($allAreEmbeddedResource && $hasEmbeddedResource) { + return array_map(fn($item) => $item->resource, $readResult); + } + + if ($hasResourceContents || $hasEmbeddedResource) { + $result = []; + foreach ($readResult as $item) { + if ($item instanceof ResourceContents) { + $result[] = $item; + } elseif ($item instanceof EmbeddedResource) { + $result[] = $item->resource; + } else { + $result = array_merge($result, $this->formatResult($item, $uri, $mimeType)); + } + } + return $result; + } + } + + if (is_string($readResult)) { + $mimeType = $mimeType ?? $this->guessMimeTypeFromString($readResult); + + return [TextResourceContents::make($uri, $mimeType, $readResult)]; + } + + if (is_resource($readResult) && get_resource_type($readResult) === 'stream') { + $result = BlobResourceContents::fromStream( + $uri, + $readResult, + $mimeType ?? 'application/octet-stream' + ); + + @fclose($readResult); + + return [$result]; + } + + if (is_array($readResult) && isset($readResult['blob']) && is_string($readResult['blob'])) { + $mimeType = $readResult['mimeType'] ?? $mimeType ?? 'application/octet-stream'; + + return [BlobResourceContents::make($uri, $mimeType, $readResult['blob'])]; + } + + if (is_array($readResult) && isset($readResult['text']) && is_string($readResult['text'])) { + $mimeType = $readResult['mimeType'] ?? $mimeType ?? 'text/plain'; + + return [TextResourceContents::make($uri, $mimeType, $readResult['text'])]; + } + + if ($readResult instanceof \SplFileInfo && $readResult->isFile() && $readResult->isReadable()) { + if ($mimeType && str_contains(strtolower($mimeType), 'text')) { + return [TextResourceContents::make($uri, $mimeType, file_get_contents($readResult->getPathname()))]; + } + + return [BlobResourceContents::fromSplFileInfo($uri, $readResult, $mimeType)]; + } + + if (is_array($readResult)) { + if ($mimeType && (str_contains(strtolower($mimeType), 'json') || + $mimeType === 'application/json')) { + try { + $jsonString = json_encode($readResult, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT); + + return [TextResourceContents::make($uri, $mimeType, $jsonString)]; + } catch (\JsonException $e) { + throw new \RuntimeException("Failed to encode array as JSON for URI '{$uri}': {$e->getMessage()}"); + } + } + + try { + $jsonString = json_encode($readResult, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT); + $mimeType = $mimeType ?? 'application/json'; + + return [TextResourceContents::make($uri, $mimeType, $jsonString)]; + } catch (\JsonException $e) { + throw new \RuntimeException("Failed to encode array as JSON for URI '{$uri}': {$e->getMessage()}"); + } + } + + throw new \RuntimeException("Cannot format resource read result for URI '{$uri}'. Handler method returned unhandled type: " . gettype($readResult)); + } + + /** Guesses MIME type from string content (very basic) */ + private function guessMimeTypeFromString(string $content): string + { + $trimmed = ltrim($content); + + if (str_starts_with($trimmed, '<') && str_ends_with(rtrim($content), '>')) { + if (str_contains($trimmed, ' $this->schema->toArray(), + ...parent::toArray(), + ]; + } + + public static function fromArray(array $data): self|false + { + try { + if (! isset($data['schema']) || ! isset($data['handler'])) { + return false; + } + + return new self( + Resource::fromArray($data['schema']), + $data['handler'], + $data['isManual'] ?? false, + ); + } catch (Throwable $e) { + return false; + } + } +} diff --git a/vendor/php-mcp/server/src/Elements/RegisteredResourceTemplate.php b/vendor/php-mcp/server/src/Elements/RegisteredResourceTemplate.php new file mode 100644 index 000000000..dc3e6a7d9 --- /dev/null +++ b/vendor/php-mcp/server/src/Elements/RegisteredResourceTemplate.php @@ -0,0 +1,327 @@ +compileTemplate(); + } + + public static function make(ResourceTemplate $schema, callable|array|string $handler, bool $isManual = false, array $completionProviders = []): self + { + return new self($schema, $handler, $isManual, $completionProviders); + } + + /** + * Gets the resource template. + * + * @return array Array of ResourceContents objects. + */ + public function read(ContainerInterface $container, string $uri): array + { + $arguments = array_merge($this->uriVariables, ['uri' => $uri]); + + $result = $this->handle($container, $arguments); + + return $this->formatResult($result, $uri, $this->schema->mimeType); + } + + public function complete(ContainerInterface $container, string $argument, string $value, SessionInterface $session): CompletionCompleteResult + { + $providerClassOrInstance = $this->completionProviders[$argument] ?? null; + if ($providerClassOrInstance === null) { + return new CompletionCompleteResult([]); + } + + if (is_string($providerClassOrInstance)) { + if (! class_exists($providerClassOrInstance)) { + throw new \RuntimeException("Completion provider class '{$providerClassOrInstance}' does not exist."); + } + + $provider = $container->get($providerClassOrInstance); + } else { + $provider = $providerClassOrInstance; + } + + $completions = $provider->getCompletions($value, $session); + + $total = count($completions); + $hasMore = $total > 100; + + $pagedCompletions = array_slice($completions, 0, 100); + + return new CompletionCompleteResult($pagedCompletions, $total, $hasMore); + } + + + public function getVariableNames(): array + { + return $this->variableNames; + } + + public function matches(string $uri): bool + { + if (preg_match($this->uriTemplateRegex, $uri, $matches)) { + $variables = []; + foreach ($this->variableNames as $varName) { + if (isset($matches[$varName])) { + $variables[$varName] = $matches[$varName]; + } + } + + $this->uriVariables = $variables; + + return true; + } + + return false; + } + + private function compileTemplate(): void + { + $this->variableNames = []; + $regexParts = []; + + $segments = preg_split('/(\{\w+\})/', $this->schema->uriTemplate, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + + foreach ($segments as $segment) { + if (preg_match('/^\{(\w+)\}$/', $segment, $matches)) { + $varName = $matches[1]; + $this->variableNames[] = $varName; + $regexParts[] = '(?P<' . $varName . '>[^/]+)'; + } else { + $regexParts[] = preg_quote($segment, '#'); + } + } + + $this->uriTemplateRegex = '#^' . implode('', $regexParts) . '$#'; + } + + /** + * Formats the raw result of a resource read operation into MCP ResourceContent items. + * + * @param mixed $readResult The raw result from the resource handler method. + * @param string $uri The URI of the resource that was read. + * @param ?string $defaultMimeType The default MIME type from the ResourceDefinition. + * @return array Array of ResourceContents objects. + * + * @throws \RuntimeException If the result cannot be formatted. + * + * Supported result types: + * - ResourceContent: Used as-is + * - EmbeddedResource: Resource is extracted from the EmbeddedResource + * - string: Converted to text content with guessed or provided MIME type + * - stream resource: Read and converted to blob with provided MIME type + * - array with 'blob' key: Used as blob content + * - array with 'text' key: Used as text content + * - SplFileInfo: Read and converted to blob + * - array: Converted to JSON if MIME type is application/json or contains 'json' + * For other MIME types, will try to convert to JSON with a warning + */ + protected function formatResult(mixed $readResult, string $uri, ?string $mimeType): array + { + if ($readResult instanceof ResourceContents) { + return [$readResult]; + } + + if ($readResult instanceof EmbeddedResource) { + return [$readResult->resource]; + } + + if (is_array($readResult)) { + if (empty($readResult)) { + return [TextResourceContents::make($uri, 'application/json', '[]')]; + } + + $allAreResourceContents = true; + $hasResourceContents = false; + $allAreEmbeddedResource = true; + $hasEmbeddedResource = false; + + foreach ($readResult as $item) { + if ($item instanceof ResourceContents) { + $hasResourceContents = true; + $allAreEmbeddedResource = false; + } elseif ($item instanceof EmbeddedResource) { + $hasEmbeddedResource = true; + $allAreResourceContents = false; + } else { + $allAreResourceContents = false; + $allAreEmbeddedResource = false; + } + } + + if ($allAreResourceContents && $hasResourceContents) { + return $readResult; + } + + if ($allAreEmbeddedResource && $hasEmbeddedResource) { + return array_map(fn($item) => $item->resource, $readResult); + } + + if ($hasResourceContents || $hasEmbeddedResource) { + $result = []; + foreach ($readResult as $item) { + if ($item instanceof ResourceContents) { + $result[] = $item; + } elseif ($item instanceof EmbeddedResource) { + $result[] = $item->resource; + } else { + $result = array_merge($result, $this->formatResult($item, $uri, $mimeType)); + } + } + return $result; + } + } + + if (is_string($readResult)) { + $mimeType = $mimeType ?? $this->guessMimeTypeFromString($readResult); + + return [TextResourceContents::make($uri, $mimeType, $readResult)]; + } + + if (is_resource($readResult) && get_resource_type($readResult) === 'stream') { + $result = BlobResourceContents::fromStream( + $uri, + $readResult, + $mimeType ?? 'application/octet-stream' + ); + + @fclose($readResult); + + return [$result]; + } + + if (is_array($readResult) && isset($readResult['blob']) && is_string($readResult['blob'])) { + $mimeType = $readResult['mimeType'] ?? $mimeType ?? 'application/octet-stream'; + + return [BlobResourceContents::make($uri, $mimeType, $readResult['blob'])]; + } + + if (is_array($readResult) && isset($readResult['text']) && is_string($readResult['text'])) { + $mimeType = $readResult['mimeType'] ?? $mimeType ?? 'text/plain'; + + return [TextResourceContents::make($uri, $mimeType, $readResult['text'])]; + } + + if ($readResult instanceof \SplFileInfo && $readResult->isFile() && $readResult->isReadable()) { + if ($mimeType && str_contains(strtolower($mimeType), 'text')) { + return [TextResourceContents::make($uri, $mimeType, file_get_contents($readResult->getPathname()))]; + } + + return [BlobResourceContents::fromSplFileInfo($uri, $readResult, $mimeType)]; + } + + if (is_array($readResult)) { + if ($mimeType && (str_contains(strtolower($mimeType), 'json') || + $mimeType === 'application/json')) { + try { + $jsonString = json_encode($readResult, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT); + + return [TextResourceContents::make($uri, $mimeType, $jsonString)]; + } catch (\JsonException $e) { + throw new \RuntimeException("Failed to encode array as JSON for URI '{$uri}': {$e->getMessage()}"); + } + } + + try { + $jsonString = json_encode($readResult, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT); + $mimeType = $mimeType ?? 'application/json'; + + return [TextResourceContents::make($uri, $mimeType, $jsonString)]; + } catch (\JsonException $e) { + throw new \RuntimeException("Failed to encode array as JSON for URI '{$uri}': {$e->getMessage()}"); + } + } + + throw new \RuntimeException("Cannot format resource read result for URI '{$uri}'. Handler method returned unhandled type: " . gettype($readResult)); + } + + /** Guesses MIME type from string content (very basic) */ + private function guessMimeTypeFromString(string $content): string + { + $trimmed = ltrim($content); + + if (str_starts_with($trimmed, '<') && str_ends_with(rtrim($content), '>')) { + if (str_contains($trimmed, 'completionProviders as $argument => $provider) { + $completionProviders[$argument] = serialize($provider); + } + + return [ + 'schema' => $this->schema->toArray(), + 'completionProviders' => $completionProviders, + ...parent::toArray(), + ]; + } + + public static function fromArray(array $data): self|false + { + try { + if (! isset($data['schema']) || ! isset($data['handler'])) { + return false; + } + + $completionProviders = []; + foreach ($data['completionProviders'] ?? [] as $argument => $provider) { + $completionProviders[$argument] = unserialize($provider); + } + + return new self( + ResourceTemplate::fromArray($data['schema']), + $data['handler'], + $data['isManual'] ?? false, + $completionProviders, + ); + } catch (Throwable $e) { + return false; + } + } +} diff --git a/vendor/php-mcp/server/src/Elements/RegisteredTool.php b/vendor/php-mcp/server/src/Elements/RegisteredTool.php new file mode 100644 index 000000000..9e35a0e22 --- /dev/null +++ b/vendor/php-mcp/server/src/Elements/RegisteredTool.php @@ -0,0 +1,140 @@ +handle($container, $arguments); + + return $this->formatResult($result); + } + + /** + * Formats the result of a tool execution into an array of MCP Content items. + * + * - If the result is already a Content object, it's wrapped in an array. + * - If the result is an array: + * - If all elements are Content objects, the array is returned as is. + * - If it's a mixed array (Content and non-Content items), non-Content items are + * individually formatted (scalars to TextContent, others to JSON TextContent). + * - If it's an array with no Content items, the entire array is JSON-encoded into a single TextContent. + * - Scalars (string, int, float, bool) are wrapped in TextContent. + * - null is represented as TextContent('(null)'). + * - Other objects are JSON-encoded and wrapped in TextContent. + * + * @param mixed $toolExecutionResult The raw value returned by the tool's PHP method. + * @return Content[] The content items for CallToolResult. + * @throws JsonException if JSON encoding fails for non-Content array/object results. + */ + protected function formatResult(mixed $toolExecutionResult): array + { + if ($toolExecutionResult instanceof Content) { + return [$toolExecutionResult]; + } + + if (is_array($toolExecutionResult)) { + if (empty($toolExecutionResult)) { + return [TextContent::make('[]')]; + } + + $allAreContent = true; + $hasContent = false; + + foreach ($toolExecutionResult as $item) { + if ($item instanceof Content) { + $hasContent = true; + } else { + $allAreContent = false; + } + } + + if ($allAreContent && $hasContent) { + return $toolExecutionResult; + } + + if ($hasContent) { + $result = []; + foreach ($toolExecutionResult as $item) { + if ($item instanceof Content) { + $result[] = $item; + } else { + $result = array_merge($result, $this->formatResult($item)); + } + } + return $result; + } + } + + if ($toolExecutionResult === null) { + return [TextContent::make('(null)')]; + } + + if (is_bool($toolExecutionResult)) { + return [TextContent::make($toolExecutionResult ? 'true' : 'false')]; + } + + if (is_scalar($toolExecutionResult)) { + return [TextContent::make($toolExecutionResult)]; + } + + $jsonResult = json_encode( + $toolExecutionResult, + JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR | JSON_INVALID_UTF8_SUBSTITUTE + ); + + return [TextContent::make($jsonResult)]; + } + + public function toArray(): array + { + return [ + 'schema' => $this->schema->toArray(), + ...parent::toArray(), + ]; + } + + public static function fromArray(array $data): self|false + { + try { + if (! isset($data['schema']) || ! isset($data['handler'])) { + return false; + } + + return new self( + Tool::fromArray($data['schema']), + $data['handler'], + $data['isManual'] ?? false, + ); + } catch (Throwable $e) { + return false; + } + } +} diff --git a/vendor/php-mcp/server/src/Exception/ConfigurationException.php b/vendor/php-mcp/server/src/Exception/ConfigurationException.php new file mode 100644 index 000000000..04e564ced --- /dev/null +++ b/vendor/php-mcp/server/src/Exception/ConfigurationException.php @@ -0,0 +1,16 @@ +data = $data; + } + + /** + * Get additional error data. + * + * @return mixed|null + */ + public function getData(): mixed + { + return $this->data; + } + + /** + * Formats the exception into a JSON-RPC 2.0 error object structure. + * Specific exceptions should override this or provide factories with correct codes. + */ + public function toJsonRpcError(string|int $id): JsonRpcError + { + $code = ($this->code >= -32768 && $this->code <= -32000) ? $this->code : Constants::INTERNAL_ERROR; + + return new JsonRpcError( + jsonrpc: '2.0', + id: $id, + code: $code, + message: $this->getMessage(), + data: $this->getData() + ); + } + + public static function parseError(string $details, ?Throwable $previous = null): self + { + return new ProtocolException('Parse error: ' . $details, Constants::PARSE_ERROR, null, $previous); + } + + public static function invalidRequest(?string $details = 'Invalid Request', ?Throwable $previous = null): self + { + return new ProtocolException($details, Constants::INVALID_REQUEST, null, $previous); + } + + public static function methodNotFound(string $methodName, ?string $message = null, ?Throwable $previous = null): self + { + return new ProtocolException($message ?? "Method not found: {$methodName}", Constants::METHOD_NOT_FOUND, null, $previous); + } + + public static function invalidParams(string $message = 'Invalid params', $data = null, ?Throwable $previous = null): self + { + // Pass data (e.g., validation errors) through + return new ProtocolException($message, Constants::INVALID_PARAMS, $data, $previous); + } + + public static function internalError(?string $details = 'Internal server error', ?Throwable $previous = null): self + { + $message = 'Internal error'; + if ($details && is_string($details)) { + $message .= ': ' . $details; + } elseif ($previous && $details === null) { + $message .= ' (See server logs)'; + } + + return new McpServerException($message, Constants::INTERNAL_ERROR, null, $previous); + } + + public static function toolExecutionFailed(string $toolName, ?Throwable $previous = null): self + { + $message = "Execution failed for tool '{$toolName}'"; + if ($previous) { + $message .= ': ' . $previous->getMessage(); + } + + return new McpServerException($message, Constants::INTERNAL_ERROR, null, $previous); + } + + public static function resourceReadFailed(string $uri, ?Throwable $previous = null): self + { + $message = "Failed to read resource '{$uri}'"; + if ($previous) { + $message .= ': ' . $previous->getMessage(); + } + + return new McpServerException($message, Constants::INTERNAL_ERROR, null, $previous); + } + + public static function promptGenerationFailed(string $promptName, ?Throwable $previous = null): self + { + $message = "Failed to generate prompt '{$promptName}'"; + if ($previous) { + $message .= ': ' . $previous->getMessage(); + } + + return new McpServerException($message, Constants::INTERNAL_ERROR, null, $previous); + } +} diff --git a/vendor/php-mcp/server/src/Exception/ProtocolException.php b/vendor/php-mcp/server/src/Exception/ProtocolException.php new file mode 100644 index 000000000..32cf04ce7 --- /dev/null +++ b/vendor/php-mcp/server/src/Exception/ProtocolException.php @@ -0,0 +1,28 @@ +code >= -32700 && $this->code <= -32600) ? $this->code : self::CODE_INVALID_REQUEST; + + return new JsonRpcError( + jsonrpc: '2.0', + id: $id, + code: $code, + message: $this->getMessage(), + data: $this->getData() + ); + } +} diff --git a/vendor/php-mcp/server/src/Exception/TransportException.php b/vendor/php-mcp/server/src/Exception/TransportException.php new file mode 100644 index 000000000..293cda37a --- /dev/null +++ b/vendor/php-mcp/server/src/Exception/TransportException.php @@ -0,0 +1,25 @@ +getMessage(), + data: null + ); + } +} diff --git a/vendor/php-mcp/server/src/Protocol.php b/vendor/php-mcp/server/src/Protocol.php new file mode 100644 index 000000000..4ca20f2f0 --- /dev/null +++ b/vendor/php-mcp/server/src/Protocol.php @@ -0,0 +1,474 @@ +logger = $this->configuration->logger; + $this->subscriptionManager ??= new SubscriptionManager($this->logger); + $this->dispatcher ??= new Dispatcher($this->configuration, $this->registry, $this->subscriptionManager); + + $this->sessionManager->on('session_deleted', function (string $sessionId) { + $this->subscriptionManager->cleanupSession($sessionId); + }); + + $this->registry->on('list_changed', function (string $listType) { + $this->handleListChanged($listType); + }); + } + + /** + * Binds this handler to a transport instance by attaching event listeners. + * Does NOT start the transport's listening process itself. + */ + public function bindTransport(ServerTransportInterface $transport): void + { + if ($this->transport !== null) { + $this->unbindTransport(); + } + + $this->transport = $transport; + + $this->listeners = [ + 'message' => [$this, 'processMessage'], + 'client_connected' => [$this, 'handleClientConnected'], + 'client_disconnected' => [$this, 'handleClientDisconnected'], + 'error' => [$this, 'handleTransportError'], + ]; + + $this->transport->on('message', $this->listeners['message']); + $this->transport->on('client_connected', $this->listeners['client_connected']); + $this->transport->on('client_disconnected', $this->listeners['client_disconnected']); + $this->transport->on('error', $this->listeners['error']); + } + + /** + * Detaches listeners from the current transport. + */ + public function unbindTransport(): void + { + if ($this->transport && ! empty($this->listeners)) { + $this->transport->removeListener('message', $this->listeners['message']); + $this->transport->removeListener('client_connected', $this->listeners['client_connected']); + $this->transport->removeListener('client_disconnected', $this->listeners['client_disconnected']); + $this->transport->removeListener('error', $this->listeners['error']); + } + + $this->transport = null; + $this->listeners = []; + } + + /** + * Handles a message received from the transport. + * + * Processes via Processor, sends Response/Error. + */ + public function processMessage(Request|Notification|BatchRequest $message, string $sessionId, array $context = []): void + { + $this->logger->debug('Message received.', ['sessionId' => $sessionId, 'message' => $message]); + + $session = $this->sessionManager->getSession($sessionId); + + if ($session === null) { + $error = Error::forInvalidRequest('Invalid or expired session. Please re-initialize the session.', $message->id); + $context['status_code'] = 404; + + $this->transport->sendMessage($error, $sessionId, $context) + ->then(function () use ($sessionId, $error, $context) { + $this->logger->debug('Response sent.', ['sessionId' => $sessionId, 'payload' => $error, 'context' => $context]); + }) + ->catch(function (Throwable $e) use ($sessionId, $error, $context) { + $this->logger->error('Failed to send response.', ['sessionId' => $sessionId, 'error' => $e->getMessage()]); + }); + + return; + } + + if ($context['stateless'] ?? false) { + $session->set('initialized', true); + $session->set('protocol_version', self::LATEST_PROTOCOL_VERSION); + $session->set('client_info', ['name' => 'stateless-client', 'version' => '1.0.0']); + } + + $response = null; + + if ($message instanceof BatchRequest) { + $response = $this->processBatchRequest($message, $session); + } elseif ($message instanceof Request) { + $response = $this->processRequest($message, $session); + } elseif ($message instanceof Notification) { + $this->processNotification($message, $session); + } + + $session->save(); + + if ($response === null) { + return; + } + + $this->transport->sendMessage($response, $sessionId, $context) + ->then(function () use ($sessionId, $response) { + $this->logger->debug('Response sent.', ['sessionId' => $sessionId, 'payload' => $response]); + }) + ->catch(function (Throwable $e) use ($sessionId) { + $this->logger->error('Failed to send response.', ['sessionId' => $sessionId, 'error' => $e->getMessage()]); + }); + } + + /** + * Process a batch message + */ + private function processBatchRequest(BatchRequest $batch, SessionInterface $session): ?BatchResponse + { + $items = []; + + foreach ($batch->getNotifications() as $notification) { + $this->processNotification($notification, $session); + } + + foreach ($batch->getRequests() as $request) { + $items[] = $this->processRequest($request, $session); + } + + return empty($items) ? null : new BatchResponse($items); + } + + /** + * Process a request message + */ + private function processRequest(Request $request, SessionInterface $session): Response|Error + { + try { + if ($request->method !== 'initialize') { + $this->assertSessionInitialized($session); + } + + $this->assertRequestCapability($request->method); + + $result = $this->dispatcher->handleRequest($request, $session); + + return Response::make($request->id, $result); + } catch (McpServerException $e) { + $this->logger->debug('MCP Processor caught McpServerException', ['method' => $request->method, 'code' => $e->getCode(), 'message' => $e->getMessage(), 'data' => $e->getData()]); + + return $e->toJsonRpcError($request->id); + } catch (Throwable $e) { + $this->logger->error('MCP Processor caught unexpected error', [ + 'method' => $request->method, + 'exception' => $e->getMessage(), + 'trace' => $e->getTraceAsString(), + ]); + + return new Error( + jsonrpc: '2.0', + id: $request->id, + code: Constants::INTERNAL_ERROR, + message: 'Internal error processing method ' . $request->method, + data: $e->getMessage() + ); + } + } + + /** + * Process a notification message + */ + private function processNotification(Notification $notification, SessionInterface $session): void + { + $method = $notification->method; + $params = $notification->params; + + try { + $this->dispatcher->handleNotification($notification, $session); + } catch (Throwable $e) { + $this->logger->error('Error while processing notification', ['method' => $method, 'exception' => $e->getMessage()]); + return; + } + } + + /** + * Send a notification to a session + */ + public function sendNotification(Notification $notification, string $sessionId): PromiseInterface + { + if ($this->transport === null) { + $this->logger->error('Cannot send notification, transport not bound', [ + 'sessionId' => $sessionId, + 'method' => $notification->method + ]); + return reject(new McpServerException('Transport not bound')); + } + + return $this->transport->sendMessage($notification, $sessionId, []) + ->then(function () { + return resolve(null); + }) + ->catch(function (Throwable $e) { + return reject(new McpServerException('Failed to send notification: ' . $e->getMessage(), previous: $e)); + }); + } + + /** + * Notify subscribers about resource content change + */ + public function notifyResourceUpdated(string $uri): void + { + $subscribers = $this->subscriptionManager->getSubscribers($uri); + + if (empty($subscribers)) { + return; + } + + $notification = ResourceUpdatedNotification::make($uri); + + foreach ($subscribers as $sessionId) { + $this->sendNotification($notification, $sessionId); + } + + $this->logger->debug("Sent resource change notification", [ + 'uri' => $uri, + 'subscriber_count' => count($subscribers) + ]); + } + + /** + * Validate that a session is initialized + */ + private function assertSessionInitialized(SessionInterface $session): void + { + if (!$session->get('initialized', false)) { + throw McpServerException::invalidRequest('Client session not initialized.'); + } + } + + /** + * Assert that a request method is enabled + */ + private function assertRequestCapability(string $method): void + { + $capabilities = $this->configuration->capabilities; + + switch ($method) { + case "ping": + case "initialize": + // No specific capability required for these methods + break; + + case 'tools/list': + case 'tools/call': + if (!$capabilities->tools) { + throw McpServerException::methodNotFound($method, 'Tools are not enabled on this server.'); + } + break; + + case 'resources/list': + case 'resources/templates/list': + case 'resources/read': + if (!$capabilities->resources) { + throw McpServerException::methodNotFound($method, 'Resources are not enabled on this server.'); + } + break; + + case 'resources/subscribe': + case 'resources/unsubscribe': + if (!$capabilities->resources) { + throw McpServerException::methodNotFound($method, 'Resources are not enabled on this server.'); + } + if (!$capabilities->resourcesSubscribe) { + throw McpServerException::methodNotFound($method, 'Resources subscription is not enabled on this server.'); + } + break; + + case 'prompts/list': + case 'prompts/get': + if (!$capabilities->prompts) { + throw McpServerException::methodNotFound($method, 'Prompts are not enabled on this server.'); + } + break; + + case 'logging/setLevel': + if (!$capabilities->logging) { + throw McpServerException::methodNotFound($method, 'Logging is not enabled on this server.'); + } + break; + + case 'completion/complete': + if (!$capabilities->completions) { + throw McpServerException::methodNotFound($method, 'Completions are not enabled on this server.'); + } + break; + + default: + break; + } + } + + private function canSendNotification(string $method): bool + { + $capabilities = $this->configuration->capabilities; + + $valid = true; + + switch ($method) { + case 'notifications/message': + if (!$capabilities->logging) { + $this->logger->warning('Logging is not enabled on this server. Notifications/message will not be sent.'); + $valid = false; + } + break; + + case "notifications/resources/updated": + case "notifications/resources/list_changed": + if (!$capabilities->resources || !$capabilities->resourcesListChanged) { + $this->logger->warning('Resources list changed notifications are not enabled on this server. Notifications/resources/list_changed will not be sent.'); + $valid = false; + } + break; + + case "notifications/tools/list_changed": + if (!$capabilities->tools || !$capabilities->toolsListChanged) { + $this->logger->warning('Tools list changed notifications are not enabled on this server. Notifications/tools/list_changed will not be sent.'); + $valid = false; + } + break; + + case "notifications/prompts/list_changed": + if (!$capabilities->prompts || !$capabilities->promptsListChanged) { + $this->logger->warning('Prompts list changed notifications are not enabled on this server. Notifications/prompts/list_changed will not be sent.'); + $valid = false; + } + break; + + case "notifications/cancelled": + // Cancellation notifications are always allowed + break; + + case "notifications/progress": + // Progress notifications are always allowed + break; + + default: + break; + } + + return $valid; + } + + /** + * Handles 'client_connected' event from the transport + */ + public function handleClientConnected(string $sessionId): void + { + $this->logger->info('Client connected', ['sessionId' => $sessionId]); + + $this->sessionManager->createSession($sessionId); + } + + /** + * Handles 'client_disconnected' event from the transport + */ + public function handleClientDisconnected(string $sessionId, ?string $reason = null): void + { + $this->logger->info('Client disconnected', ['clientId' => $sessionId, 'reason' => $reason ?? 'N/A']); + + $this->sessionManager->deleteSession($sessionId); + } + + /** + * Handle list changed event from registry + */ + public function handleListChanged(string $listType): void + { + $listChangeUri = "mcp://changes/{$listType}"; + + $subscribers = $this->subscriptionManager->getSubscribers($listChangeUri); + if (empty($subscribers)) { + return; + } + + $notification = match ($listType) { + 'resources' => ResourceListChangedNotification::make(), + 'tools' => ToolListChangedNotification::make(), + 'prompts' => PromptListChangedNotification::make(), + 'roots' => RootsListChangedNotification::make(), + default => throw new \InvalidArgumentException("Invalid list type: {$listType}"), + }; + + if (!$this->canSendNotification($notification->method)) { + return; + } + + foreach ($subscribers as $sessionId) { + $this->sendNotification($notification, $sessionId); + } + + $this->logger->debug("Sent list change notification", [ + 'list_type' => $listType, + 'subscriber_count' => count($subscribers) + ]); + } + + /** + * Handles 'error' event from the transport + */ + public function handleTransportError(Throwable $error, ?string $clientId = null): void + { + $context = ['error' => $error->getMessage(), 'exception_class' => get_class($error)]; + + if ($clientId) { + $context['clientId'] = $clientId; + $this->logger->error('Transport error for client', $context); + } else { + $this->logger->error('General transport error', $context); + } + } +} diff --git a/vendor/php-mcp/server/src/Registry.php b/vendor/php-mcp/server/src/Registry.php new file mode 100644 index 000000000..07125cee8 --- /dev/null +++ b/vendor/php-mcp/server/src/Registry.php @@ -0,0 +1,478 @@ + */ + private array $tools = []; + + /** @var array */ + private array $resources = []; + + /** @var array */ + private array $prompts = []; + + /** @var array */ + private array $resourceTemplates = []; + + private array $listHashes = [ + 'tools' => '', + 'resources' => '', + 'resource_templates' => '', + 'prompts' => '', + ]; + + private bool $notificationsEnabled = true; + + public function __construct( + protected LoggerInterface $logger, + protected ?CacheInterface $cache = null, + ) { + $this->load(); + $this->computeAllHashes(); + } + + /** + * Compute hashes for all lists for change detection + */ + private function computeAllHashes(): void + { + $this->listHashes['tools'] = $this->computeHash($this->tools); + $this->listHashes['resources'] = $this->computeHash($this->resources); + $this->listHashes['resource_templates'] = $this->computeHash($this->resourceTemplates); + $this->listHashes['prompts'] = $this->computeHash($this->prompts); + } + + /** + * Compute a stable hash for a collection + */ + private function computeHash(array $collection): string + { + if (empty($collection)) { + return ''; + } + + ksort($collection); + return md5(json_encode($collection)); + } + + public function load(): void + { + if ($this->cache === null) { + return; + } + + $this->clear(false); + + try { + $cached = $this->cache->get(self::DISCOVERED_ELEMENTS_CACHE_KEY); + + if (!is_array($cached)) { + $this->logger->warning('Invalid or missing data found in registry cache, ignoring.', ['key' => self::DISCOVERED_ELEMENTS_CACHE_KEY, 'type' => gettype($cached)]); + return; + } + + $loadCount = 0; + + foreach ($cached['tools'] ?? [] as $toolData) { + $cachedTool = RegisteredTool::fromArray(json_decode($toolData, true)); + if ($cachedTool === false) { + $this->logger->warning('Invalid or missing data found in registry cache, ignoring.', ['key' => self::DISCOVERED_ELEMENTS_CACHE_KEY, 'type' => gettype($cached)]); + continue; + } + + $toolName = $cachedTool->schema->name; + $existingTool = $this->tools[$toolName] ?? null; + + if ($existingTool && $existingTool->isManual) { + $this->logger->debug("Skipping cached tool '{$toolName}' as manual version exists."); + continue; + } + + $this->tools[$toolName] = $cachedTool; + $loadCount++; + } + + foreach ($cached['resources'] ?? [] as $resourceData) { + $cachedResource = RegisteredResource::fromArray(json_decode($resourceData, true)); + if ($cachedResource === false) { + $this->logger->warning('Invalid or missing data found in registry cache, ignoring.', ['key' => self::DISCOVERED_ELEMENTS_CACHE_KEY, 'type' => gettype($cached)]); + continue; + } + + $uri = $cachedResource->schema->uri; + $existingResource = $this->resources[$uri] ?? null; + + if ($existingResource && $existingResource->isManual) { + $this->logger->debug("Skipping cached resource '{$uri}' as manual version exists."); + continue; + } + + $this->resources[$uri] = $cachedResource; + $loadCount++; + } + + foreach ($cached['prompts'] ?? [] as $promptData) { + $cachedPrompt = RegisteredPrompt::fromArray(json_decode($promptData, true)); + if ($cachedPrompt === false) { + $this->logger->warning('Invalid or missing data found in registry cache, ignoring.', ['key' => self::DISCOVERED_ELEMENTS_CACHE_KEY, 'type' => gettype($cached)]); + continue; + } + + $promptName = $cachedPrompt->schema->name; + $existingPrompt = $this->prompts[$promptName] ?? null; + + if ($existingPrompt && $existingPrompt->isManual) { + $this->logger->debug("Skipping cached prompt '{$promptName}' as manual version exists."); + continue; + } + + $this->prompts[$promptName] = $cachedPrompt; + $loadCount++; + } + + foreach ($cached['resourceTemplates'] ?? [] as $templateData) { + $cachedTemplate = RegisteredResourceTemplate::fromArray(json_decode($templateData, true)); + if ($cachedTemplate === false) { + $this->logger->warning('Invalid or missing data found in registry cache, ignoring.', ['key' => self::DISCOVERED_ELEMENTS_CACHE_KEY, 'type' => gettype($cached)]); + continue; + } + + $uriTemplate = $cachedTemplate->schema->uriTemplate; + $existingTemplate = $this->resourceTemplates[$uriTemplate] ?? null; + + if ($existingTemplate && $existingTemplate->isManual) { + $this->logger->debug("Skipping cached template '{$uriTemplate}' as manual version exists."); + continue; + } + + $this->resourceTemplates[$uriTemplate] = $cachedTemplate; + $loadCount++; + } + + $this->logger->debug("Loaded {$loadCount} elements from cache."); + } catch (CacheInvalidArgumentException $e) { + $this->logger->error('Invalid registry cache key used.', ['key' => self::DISCOVERED_ELEMENTS_CACHE_KEY, 'exception' => $e]); + } catch (Throwable $e) { + $this->logger->error('Unexpected error loading from cache.', ['key' => self::DISCOVERED_ELEMENTS_CACHE_KEY, 'exception' => $e]); + } + } + + public function registerTool(Tool $tool, callable|array|string $handler, bool $isManual = false): void + { + $toolName = $tool->name; + $existing = $this->tools[$toolName] ?? null; + + if ($existing && ! $isManual && $existing->isManual) { + $this->logger->debug("Ignoring discovered tool '{$toolName}' as it conflicts with a manually registered one."); + + return; + } + + $this->tools[$toolName] = RegisteredTool::make($tool, $handler, $isManual); + + $this->checkAndEmitChange('tools', $this->tools); + } + + public function registerResource(Resource $resource, callable|array|string $handler, bool $isManual = false): void + { + $uri = $resource->uri; + $existing = $this->resources[$uri] ?? null; + + if ($existing && ! $isManual && $existing->isManual) { + $this->logger->debug("Ignoring discovered resource '{$uri}' as it conflicts with a manually registered one."); + + return; + } + + $this->resources[$uri] = RegisteredResource::make($resource, $handler, $isManual); + + $this->checkAndEmitChange('resources', $this->resources); + } + + public function registerResourceTemplate( + ResourceTemplate $template, + callable|array|string $handler, + array $completionProviders = [], + bool $isManual = false, + ): void { + $uriTemplate = $template->uriTemplate; + $existing = $this->resourceTemplates[$uriTemplate] ?? null; + + if ($existing && ! $isManual && $existing->isManual) { + $this->logger->debug("Ignoring discovered template '{$uriTemplate}' as it conflicts with a manually registered one."); + + return; + } + + $this->resourceTemplates[$uriTemplate] = RegisteredResourceTemplate::make($template, $handler, $isManual, $completionProviders); + + $this->checkAndEmitChange('resource_templates', $this->resourceTemplates); + } + + public function registerPrompt( + Prompt $prompt, + callable|array|string $handler, + array $completionProviders = [], + bool $isManual = false, + ): void { + $promptName = $prompt->name; + $existing = $this->prompts[$promptName] ?? null; + + if ($existing && ! $isManual && $existing->isManual) { + $this->logger->debug("Ignoring discovered prompt '{$promptName}' as it conflicts with a manually registered one."); + + return; + } + + $this->prompts[$promptName] = RegisteredPrompt::make($prompt, $handler, $isManual, $completionProviders); + + $this->checkAndEmitChange('prompts', $this->prompts); + } + + public function enableNotifications(): void + { + $this->notificationsEnabled = true; + } + + public function disableNotifications(): void + { + $this->notificationsEnabled = false; + } + + /** + * Check if a list has changed and emit event if needed + */ + private function checkAndEmitChange(string $listType, array $collection): void + { + if (! $this->notificationsEnabled) { + return; + } + + $newHash = $this->computeHash($collection); + + if ($newHash !== $this->listHashes[$listType]) { + $this->listHashes[$listType] = $newHash; + $this->emit('list_changed', [$listType]); + } + } + + public function save(): bool + { + if ($this->cache === null) { + return false; + } + + $discoveredData = [ + 'tools' => [], + 'resources' => [], + 'prompts' => [], + 'resourceTemplates' => [], + ]; + + foreach ($this->tools as $name => $tool) { + if (! $tool->isManual) { + if ($tool->handler instanceof \Closure) { + $this->logger->warning("Skipping closure tool from cache: {$name}"); + continue; + } + $discoveredData['tools'][$name] = json_encode($tool); + } + } + + foreach ($this->resources as $uri => $resource) { + if (! $resource->isManual) { + if ($resource->handler instanceof \Closure) { + $this->logger->warning("Skipping closure resource from cache: {$uri}"); + continue; + } + $discoveredData['resources'][$uri] = json_encode($resource); + } + } + + foreach ($this->prompts as $name => $prompt) { + if (! $prompt->isManual) { + if ($prompt->handler instanceof \Closure) { + $this->logger->warning("Skipping closure prompt from cache: {$name}"); + continue; + } + $discoveredData['prompts'][$name] = json_encode($prompt); + } + } + + foreach ($this->resourceTemplates as $uriTemplate => $template) { + if (! $template->isManual) { + if ($template->handler instanceof \Closure) { + $this->logger->warning("Skipping closure template from cache: {$uriTemplate}"); + continue; + } + $discoveredData['resourceTemplates'][$uriTemplate] = json_encode($template); + } + } + + try { + $success = $this->cache->set(self::DISCOVERED_ELEMENTS_CACHE_KEY, $discoveredData); + + if ($success) { + $this->logger->debug('Registry elements saved to cache.', ['key' => self::DISCOVERED_ELEMENTS_CACHE_KEY]); + } else { + $this->logger->warning('Registry cache set operation returned false.', ['key' => self::DISCOVERED_ELEMENTS_CACHE_KEY]); + } + + return $success; + } catch (CacheInvalidArgumentException $e) { + $this->logger->error('Invalid cache key or value during save.', ['key' => self::DISCOVERED_ELEMENTS_CACHE_KEY, 'exception' => $e]); + + return false; + } catch (Throwable $e) { + $this->logger->error('Unexpected error saving to cache.', ['key' => self::DISCOVERED_ELEMENTS_CACHE_KEY, 'exception' => $e]); + + return false; + } + } + + /** Checks if any elements (manual or discovered) are currently registered. */ + public function hasElements(): bool + { + return ! empty($this->tools) + || ! empty($this->resources) + || ! empty($this->prompts) + || ! empty($this->resourceTemplates); + } + + /** + * Clear discovered elements from registry + * + * @param bool $includeCache Whether to clear the cache as well (default: true) + */ + public function clear(bool $includeCache = true): void + { + if ($includeCache && $this->cache !== null) { + try { + $this->cache->delete(self::DISCOVERED_ELEMENTS_CACHE_KEY); + $this->logger->debug('Registry cache cleared.'); + } catch (Throwable $e) { + $this->logger->error('Error clearing registry cache.', ['exception' => $e]); + } + } + + $clearCount = 0; + + foreach ($this->tools as $name => $tool) { + if (! $tool->isManual) { + unset($this->tools[$name]); + $clearCount++; + } + } + foreach ($this->resources as $uri => $resource) { + if (! $resource->isManual) { + unset($this->resources[$uri]); + $clearCount++; + } + } + foreach ($this->prompts as $name => $prompt) { + if (! $prompt->isManual) { + unset($this->prompts[$name]); + $clearCount++; + } + } + foreach ($this->resourceTemplates as $uriTemplate => $template) { + if (! $template->isManual) { + unset($this->resourceTemplates[$uriTemplate]); + $clearCount++; + } + } + + if ($clearCount > 0) { + $this->logger->debug("Removed {$clearCount} discovered elements from internal registry."); + } + } + + /** @return RegisteredTool|null */ + public function getTool(string $name): ?RegisteredTool + { + return $this->tools[$name] ?? null; + } + + /** @return RegisteredResource|RegisteredResourceTemplate|null */ + public function getResource(string $uri, bool $includeTemplates = true): RegisteredResource|RegisteredResourceTemplate|null + { + $registration = $this->resources[$uri] ?? null; + if ($registration) { + return $registration; + } + + if (! $includeTemplates) { + return null; + } + + foreach ($this->resourceTemplates as $template) { + if ($template->matches($uri)) { + return $template; + } + } + + $this->logger->debug('No resource matched URI.', ['uri' => $uri]); + + return null; + } + + /** @return RegisteredResourceTemplate|null */ + public function getResourceTemplate(string $uriTemplate): ?RegisteredResourceTemplate + { + return $this->resourceTemplates[$uriTemplate] ?? null; + } + + /** @return RegisteredPrompt|null */ + public function getPrompt(string $name): ?RegisteredPrompt + { + return $this->prompts[$name] ?? null; + } + + /** @return array */ + public function getTools(): array + { + return array_map(fn($tool) => $tool->schema, $this->tools); + } + + /** @return array */ + public function getResources(): array + { + return array_map(fn($resource) => $resource->schema, $this->resources); + } + + /** @return array */ + public function getPrompts(): array + { + return array_map(fn($prompt) => $prompt->schema, $this->prompts); + } + + /** @return array */ + public function getResourceTemplates(): array + { + return array_map(fn($template) => $template->schema, $this->resourceTemplates); + } +} diff --git a/vendor/php-mcp/server/src/Server.php b/vendor/php-mcp/server/src/Server.php new file mode 100644 index 000000000..9ef162d0a --- /dev/null +++ b/vendor/php-mcp/server/src/Server.php @@ -0,0 +1,232 @@ +...->build(). + * + * @param Configuration $configuration Core configuration and dependencies. + * @param Registry $registry Holds registered MCP element definitions. + * @param Protocol $protocol Handles MCP requests and responses. + */ + public function __construct( + protected readonly Configuration $configuration, + protected readonly Registry $registry, + protected readonly Protocol $protocol, + protected readonly SessionManager $sessionManager, + ) { + } + + public static function make(): ServerBuilder + { + return new ServerBuilder(); + } + + /** + * Runs the attribute discovery process based on the configuration + * provided during build time. Caches results if cache is available. + * Can be called explicitly, but is also called by ServerBuilder::build() + * if discovery paths are configured. + * + * @param bool $force Re-run discovery even if already run. + * @param bool $useCache Attempt to load from/save to cache. Defaults to true if cache is available. + * + * @throws DiscoveryException If discovery process encounters errors. + * @throws ConfigurationException If discovery paths were not configured. + */ + public function discover( + string $basePath, + array $scanDirs = ['.', 'src'], + array $excludeDirs = [], + bool $force = false, + bool $saveToCache = true, + ?Discoverer $discoverer = null + ): void { + $realBasePath = realpath($basePath); + if ($realBasePath === false || ! is_dir($realBasePath)) { + throw new \InvalidArgumentException("Invalid discovery base path provided to discover(): {$basePath}"); + } + + $excludeDirs = array_merge($excludeDirs, ['vendor', 'tests', 'test', 'storage', 'cache', 'samples', 'docs', 'node_modules', '.git', '.svn']); + + if ($this->discoveryRan && ! $force) { + $this->configuration->logger->debug('Discovery skipped: Already run or loaded from cache.'); + + return; + } + + $cacheAvailable = $this->configuration->cache !== null; + $shouldSaveCache = $saveToCache && $cacheAvailable; + + $this->configuration->logger->info('Starting MCP element discovery...', [ + 'basePath' => $realBasePath, + 'force' => $force, + 'saveToCache' => $shouldSaveCache, + ]); + + $this->registry->clear(); + + try { + $discoverer ??= new Discoverer($this->registry, $this->configuration->logger); + + $discoverer->discover($realBasePath, $scanDirs, $excludeDirs); + + $this->discoveryRan = true; + + if ($shouldSaveCache) { + $this->registry->save(); + } + } catch (Throwable $e) { + $this->discoveryRan = false; + $this->configuration->logger->critical('MCP element discovery failed.', ['exception' => $e]); + throw new DiscoveryException("Element discovery failed: {$e->getMessage()}", $e->getCode(), $e); + } + } + + /** + * Binds the server's MCP logic to the provided transport and starts the transport's listener, + * then runs the event loop, making this a BLOCKING call suitable for standalone servers. + * + * For framework integration where the loop is managed externally, use `getProtocol()` + * and bind it to your framework's transport mechanism manually. + * + * @param ServerTransportInterface $transport The transport to listen with. + * + * @throws LogicException If called after already listening. + * @throws Throwable If transport->listen() fails immediately. + */ + public function listen(ServerTransportInterface $transport, bool $runLoop = true): void + { + if ($this->isListening) { + throw new LogicException('Server is already listening via a transport.'); + } + + $this->warnIfNoElements(); + + if ($transport instanceof LoggerAwareInterface) { + $transport->setLogger($this->configuration->logger); + } + if ($transport instanceof LoopAwareInterface) { + $transport->setLoop($this->configuration->loop); + } + + $protocol = $this->getProtocol(); + + $closeHandlerCallback = function (?string $reason = null) use ($protocol) { + $this->isListening = false; + $this->configuration->logger->info('Transport closed.', ['reason' => $reason ?? 'N/A']); + $protocol->unbindTransport(); + $this->configuration->loop->stop(); + }; + + $transport->once('close', $closeHandlerCallback); + + $protocol->bindTransport($transport); + + try { + $transport->listen(); + + $this->isListening = true; + + if ($runLoop) { + $this->sessionManager->startGcTimer(); + + $this->configuration->loop->run(); + + $this->endListen($transport); + } + } catch (Throwable $e) { + $this->configuration->logger->critical('Failed to start listening or event loop crashed.', ['exception' => $e->getMessage()]); + $this->endListen($transport); + throw $e; + } + } + + public function endListen(ServerTransportInterface $transport): void + { + $protocol = $this->getProtocol(); + + $protocol->unbindTransport(); + + $this->sessionManager->stopGcTimer(); + + $transport->removeAllListeners('close'); + $transport->close(); + + $this->isListening = false; + $this->configuration->logger->info("Server '{$this->configuration->serverInfo->name}' listener shut down."); + } + + /** + * Warns if no MCP elements are registered and discovery has not been run. + */ + protected function warnIfNoElements(): void + { + if (! $this->registry->hasElements() && ! $this->discoveryRan) { + $this->configuration->logger->warning( + 'Starting listener, but no MCP elements are registered and discovery has not been run. ' . + 'Call $server->discover(...) at least once to find and cache elements before listen().' + ); + } elseif (! $this->registry->hasElements() && $this->discoveryRan) { + $this->configuration->logger->warning( + 'Starting listener, but no MCP elements were found after discovery/cache load.' + ); + } + } + + /** + * Gets the Configuration instance associated with this server. + */ + public function getConfiguration(): Configuration + { + return $this->configuration; + } + + /** + * Gets the Registry instance associated with this server. + */ + public function getRegistry(): Registry + { + return $this->registry; + } + + /** + * Gets the Protocol instance associated with this server. + */ + public function getProtocol(): Protocol + { + return $this->protocol; + } + + public function getSessionManager(): SessionManager + { + return $this->sessionManager; + } +} diff --git a/vendor/php-mcp/server/src/ServerBuilder.php b/vendor/php-mcp/server/src/ServerBuilder.php new file mode 100644 index 000000000..dacfda312 --- /dev/null +++ b/vendor/php-mcp/server/src/ServerBuilder.php @@ -0,0 +1,526 @@ + */ + private array $manualTools = []; + + /** @var array< + * array{handler: array|string|Closure, + * uri: string, + * name: string|null, + * description: string|null, + * mimeType: string|null, + * size: int|null, + * annotations: Annotations|null} + * > */ + private array $manualResources = []; + + /** @var array< + * array{handler: array|string|Closure, + * uriTemplate: string, + * name: string|null, + * description: string|null, + * mimeType: string|null, + * annotations: Annotations|null} + * > */ + private array $manualResourceTemplates = []; + + /** @var array< + * array{handler: array|string|Closure, + * name: string|null, + * description: string|null} + * > */ + private array $manualPrompts = []; + + public function __construct() {} + + /** + * Sets the server's identity. Required. + */ + public function withServerInfo(string $name, string $version): self + { + $this->serverInfo = Implementation::make(name: trim($name), version: trim($version)); + + return $this; + } + + /** + * Configures the server's declared capabilities. + */ + public function withCapabilities(ServerCapabilities $capabilities): self + { + $this->capabilities = $capabilities; + + return $this; + } + + /** + * Configures the server's pagination limit. + */ + public function withPaginationLimit(int $paginationLimit): self + { + $this->paginationLimit = $paginationLimit; + + return $this; + } + + /** + * Configures the instructions describing how to use the server and its features. + * + * This can be used by clients to improve the LLM's understanding of available tools, resources, + * etc. It can be thought of like a "hint" to the model. For example, this information MAY + * be added to the system prompt. + */ + public function withInstructions(?string $instructions): self + { + $this->instructions = $instructions; + + return $this; + } + + /** + * Provides a PSR-3 logger instance. Defaults to NullLogger. + */ + public function withLogger(LoggerInterface $logger): self + { + $this->logger = $logger; + + return $this; + } + + /** + * Provides a PSR-16 cache instance used for all internal caching. + */ + public function withCache(CacheInterface $cache): self + { + $this->cache = $cache; + + return $this; + } + + /** + * Configures session handling with a specific driver. + * + * @param 'array' | 'cache' $driver The session driver: 'array' for in-memory sessions, 'cache' for cache-backed sessions + * @param int $ttl Session time-to-live in seconds. Defaults to 3600. + */ + public function withSession(string $driver, int $ttl = 3600): self + { + if (!in_array($driver, ['array', 'cache'], true)) { + throw new \InvalidArgumentException( + "Unsupported session driver '{$driver}'. Only 'array' and 'cache' drivers are supported. " . + "For custom session handling, use withSessionHandler() instead." + ); + } + + $this->sessionDriver = $driver; + $this->sessionTtl = $ttl; + + return $this; + } + + /** + * Provides a custom session handler. + */ + public function withSessionHandler(SessionHandlerInterface $sessionHandler, int $sessionTtl = 3600): self + { + $this->sessionHandler = $sessionHandler; + $this->sessionTtl = $sessionTtl; + + return $this; + } + + /** + * Provides a PSR-11 DI container, primarily for resolving user-defined handler classes. + * Defaults to a basic internal container. + */ + public function withContainer(ContainerInterface $container): self + { + $this->container = $container; + + return $this; + } + + /** + * Provides a ReactPHP Event Loop instance. Defaults to Loop::get(). + */ + public function withLoop(LoopInterface $loop): self + { + $this->loop = $loop; + + return $this; + } + + /** + * Manually registers a tool handler. + */ + public function withTool(callable|array|string $handler, ?string $name = null, ?string $description = null, ?ToolAnnotations $annotations = null, ?array $inputSchema = null): self + { + $this->manualTools[] = compact('handler', 'name', 'description', 'annotations', 'inputSchema'); + + return $this; + } + + /** + * Manually registers a resource handler. + */ + public function withResource(callable|array|string $handler, string $uri, ?string $name = null, ?string $description = null, ?string $mimeType = null, ?int $size = null, ?Annotations $annotations = null): self + { + $this->manualResources[] = compact('handler', 'uri', 'name', 'description', 'mimeType', 'size', 'annotations'); + + return $this; + } + + /** + * Manually registers a resource template handler. + */ + public function withResourceTemplate(callable|array|string $handler, string $uriTemplate, ?string $name = null, ?string $description = null, ?string $mimeType = null, ?Annotations $annotations = null): self + { + $this->manualResourceTemplates[] = compact('handler', 'uriTemplate', 'name', 'description', 'mimeType', 'annotations'); + + return $this; + } + + /** + * Manually registers a prompt handler. + */ + public function withPrompt(callable|array|string $handler, ?string $name = null, ?string $description = null): self + { + $this->manualPrompts[] = compact('handler', 'name', 'description'); + + return $this; + } + + /** + * Builds the fully configured Server instance. + * + * @throws ConfigurationException If required configuration is missing. + */ + public function build(): Server + { + if ($this->serverInfo === null) { + throw new ConfigurationException('Server name and version must be provided using withServerInfo().'); + } + + $loop = $this->loop ?? Loop::get(); + $cache = $this->cache; + $logger = $this->logger ?? new NullLogger(); + $container = $this->container ?? new BasicContainer(); + $capabilities = $this->capabilities ?? ServerCapabilities::make(); + + $configuration = new Configuration( + serverInfo: $this->serverInfo, + capabilities: $capabilities, + logger: $logger, + loop: $loop, + cache: $cache, + container: $container, + paginationLimit: $this->paginationLimit ?? 50, + instructions: $this->instructions, + ); + + $sessionHandler = $this->createSessionHandler(); + $sessionManager = new SessionManager($sessionHandler, $logger, $loop, $this->sessionTtl); + $registry = new Registry($logger, $cache, $sessionManager); + $protocol = new Protocol($configuration, $registry, $sessionManager); + + $registry->disableNotifications(); + + $this->registerManualElements($registry, $logger); + + $registry->enableNotifications(); + + $server = new Server($configuration, $registry, $protocol, $sessionManager); + + return $server; + } + + /** + * Helper to perform the actual registration based on stored data. + * Moved into the builder. + */ + private function registerManualElements(Registry $registry, LoggerInterface $logger): void + { + if (empty($this->manualTools) && empty($this->manualResources) && empty($this->manualResourceTemplates) && empty($this->manualPrompts)) { + return; + } + + $docBlockParser = new Utils\DocBlockParser($logger); + $schemaGenerator = new Utils\SchemaGenerator($docBlockParser); + + // Register Tools + foreach ($this->manualTools as $data) { + try { + $reflection = HandlerResolver::resolve($data['handler']); + + if ($reflection instanceof \ReflectionFunction) { + $name = $data['name'] ?? 'closure_tool_' . spl_object_id($data['handler']); + $description = $data['description'] ?? null; + } else { + $classShortName = $reflection->getDeclaringClass()->getShortName(); + $methodName = $reflection->getName(); + $docBlock = $docBlockParser->parseDocBlock($reflection->getDocComment() ?? null); + + $name = $data['name'] ?? ($methodName === '__invoke' ? $classShortName : $methodName); + $description = $data['description'] ?? $docBlockParser->getSummary($docBlock) ?? null; + } + + $inputSchema = $data['inputSchema'] ?? $schemaGenerator->generate($reflection); + + $tool = Tool::make($name, $inputSchema, $description, $data['annotations']); + $registry->registerTool($tool, $data['handler'], true); + + $handlerDesc = $data['handler'] instanceof \Closure ? 'Closure' : (is_array($data['handler']) ? implode('::', $data['handler']) : $data['handler']); + $logger->debug("Registered manual tool {$name} from handler {$handlerDesc}"); + } catch (Throwable $e) { + $logger->error('Failed to register manual tool', ['handler' => $data['handler'], 'name' => $data['name'], 'exception' => $e]); + throw new ConfigurationException("Error registering manual tool '{$data['name']}': {$e->getMessage()}", 0, $e); + } + } + + // Register Resources + foreach ($this->manualResources as $data) { + try { + $reflection = HandlerResolver::resolve($data['handler']); + + if ($reflection instanceof \ReflectionFunction) { + $name = $data['name'] ?? 'closure_resource_' . spl_object_id($data['handler']); + $description = $data['description'] ?? null; + } else { + $classShortName = $reflection->getDeclaringClass()->getShortName(); + $methodName = $reflection->getName(); + $docBlock = $docBlockParser->parseDocBlock($reflection->getDocComment() ?? null); + + $name = $data['name'] ?? ($methodName === '__invoke' ? $classShortName : $methodName); + $description = $data['description'] ?? $docBlockParser->getSummary($docBlock) ?? null; + } + + $uri = $data['uri']; + $mimeType = $data['mimeType']; + $size = $data['size']; + $annotations = $data['annotations']; + + $resource = Resource::make($uri, $name, $description, $mimeType, $annotations, $size); + $registry->registerResource($resource, $data['handler'], true); + + $handlerDesc = $data['handler'] instanceof \Closure ? 'Closure' : (is_array($data['handler']) ? implode('::', $data['handler']) : $data['handler']); + $logger->debug("Registered manual resource {$name} from handler {$handlerDesc}"); + } catch (Throwable $e) { + $logger->error('Failed to register manual resource', ['handler' => $data['handler'], 'uri' => $data['uri'], 'exception' => $e]); + throw new ConfigurationException("Error registering manual resource '{$data['uri']}': {$e->getMessage()}", 0, $e); + } + } + + // Register Templates + foreach ($this->manualResourceTemplates as $data) { + try { + $reflection = HandlerResolver::resolve($data['handler']); + + if ($reflection instanceof \ReflectionFunction) { + $name = $data['name'] ?? 'closure_template_' . spl_object_id($data['handler']); + $description = $data['description'] ?? null; + } else { + $classShortName = $reflection->getDeclaringClass()->getShortName(); + $methodName = $reflection->getName(); + $docBlock = $docBlockParser->parseDocBlock($reflection->getDocComment() ?? null); + + $name = $data['name'] ?? ($methodName === '__invoke' ? $classShortName : $methodName); + $description = $data['description'] ?? $docBlockParser->getSummary($docBlock) ?? null; + } + + $uriTemplate = $data['uriTemplate']; + $mimeType = $data['mimeType']; + $annotations = $data['annotations']; + + $template = ResourceTemplate::make($uriTemplate, $name, $description, $mimeType, $annotations); + $completionProviders = $this->getCompletionProviders($reflection); + $registry->registerResourceTemplate($template, $data['handler'], $completionProviders, true); + + $handlerDesc = $data['handler'] instanceof \Closure ? 'Closure' : (is_array($data['handler']) ? implode('::', $data['handler']) : $data['handler']); + $logger->debug("Registered manual template {$name} from handler {$handlerDesc}"); + } catch (Throwable $e) { + $logger->error('Failed to register manual template', ['handler' => $data['handler'], 'uriTemplate' => $data['uriTemplate'], 'exception' => $e]); + throw new ConfigurationException("Error registering manual resource template '{$data['uriTemplate']}': {$e->getMessage()}", 0, $e); + } + } + + // Register Prompts + foreach ($this->manualPrompts as $data) { + try { + $reflection = HandlerResolver::resolve($data['handler']); + + if ($reflection instanceof \ReflectionFunction) { + $name = $data['name'] ?? 'closure_prompt_' . spl_object_id($data['handler']); + $description = $data['description'] ?? null; + } else { + $classShortName = $reflection->getDeclaringClass()->getShortName(); + $methodName = $reflection->getName(); + $docBlock = $docBlockParser->parseDocBlock($reflection->getDocComment() ?? null); + + $name = $data['name'] ?? ($methodName === '__invoke' ? $classShortName : $methodName); + $description = $data['description'] ?? $docBlockParser->getSummary($docBlock) ?? null; + } + + $arguments = []; + $paramTags = $reflection instanceof \ReflectionMethod ? $docBlockParser->getParamTags($docBlockParser->parseDocBlock($reflection->getDocComment() ?? null)) : []; + foreach ($reflection->getParameters() as $param) { + $reflectionType = $param->getType(); + + // Basic DI check (heuristic) + if ($reflectionType instanceof \ReflectionNamedType && ! $reflectionType->isBuiltin()) { + continue; + } + + $paramTag = $paramTags['$' . $param->getName()] ?? null; + $arguments[] = PromptArgument::make( + name: $param->getName(), + description: $paramTag ? trim((string) $paramTag->getDescription()) : null, + required: ! $param->isOptional() && ! $param->isDefaultValueAvailable() + ); + } + + $prompt = Prompt::make($name, $description, $arguments); + $completionProviders = $this->getCompletionProviders($reflection); + $registry->registerPrompt($prompt, $data['handler'], $completionProviders, true); + + $handlerDesc = $data['handler'] instanceof \Closure ? 'Closure' : (is_array($data['handler']) ? implode('::', $data['handler']) : $data['handler']); + $logger->debug("Registered manual prompt {$name} from handler {$handlerDesc}"); + } catch (Throwable $e) { + $logger->error('Failed to register manual prompt', ['handler' => $data['handler'], 'name' => $data['name'], 'exception' => $e]); + throw new ConfigurationException("Error registering manual prompt '{$data['name']}': {$e->getMessage()}", 0, $e); + } + } + + $logger->debug('Manual element registration complete.'); + } + + /** + * Creates the appropriate session handler based on configuration. + * + * @throws ConfigurationException If cache driver is selected but no cache is provided + */ + private function createSessionHandler(): SessionHandlerInterface + { + // If a custom session handler was provided, use it + if ($this->sessionHandler !== null) { + return $this->sessionHandler; + } + + // If no session driver was specified, default to array + if ($this->sessionDriver === null) { + return new ArraySessionHandler($this->sessionTtl ?? 3600); + } + + // Create handler based on driver + return match ($this->sessionDriver) { + 'array' => new ArraySessionHandler($this->sessionTtl ?? 3600), + 'cache' => $this->createCacheSessionHandler(), + default => throw new ConfigurationException("Unsupported session driver: {$this->sessionDriver}") + }; + } + + /** + * Creates a cache-based session handler. + * + * @throws ConfigurationException If no cache is configured + */ + private function createCacheSessionHandler(): CacheSessionHandler + { + if ($this->cache === null) { + throw new ConfigurationException( + "Cache session driver requires a cache instance. Please configure a cache using withCache() before using withSession('cache')." + ); + } + + return new CacheSessionHandler($this->cache, $this->sessionTtl ?? 3600); + } + + private function getCompletionProviders(\ReflectionMethod|\ReflectionFunction $reflection): array + { + $completionProviders = []; + foreach ($reflection->getParameters() as $param) { + $reflectionType = $param->getType(); + if ($reflectionType instanceof \ReflectionNamedType && !$reflectionType->isBuiltin()) { + continue; + } + + $completionAttributes = $param->getAttributes(CompletionProvider::class, \ReflectionAttribute::IS_INSTANCEOF); + if (!empty($completionAttributes)) { + $attributeInstance = $completionAttributes[0]->newInstance(); + + if ($attributeInstance->provider) { + $completionProviders[$param->getName()] = $attributeInstance->provider; + } elseif ($attributeInstance->providerClass) { + $completionProviders[$param->getName()] = $attributeInstance->providerClass; + } elseif ($attributeInstance->values) { + $completionProviders[$param->getName()] = new ListCompletionProvider($attributeInstance->values); + } elseif ($attributeInstance->enum) { + $completionProviders[$param->getName()] = new EnumCompletionProvider($attributeInstance->enum); + } + } + } + + return $completionProviders; + } +} diff --git a/vendor/php-mcp/server/src/Session/ArraySessionHandler.php b/vendor/php-mcp/server/src/Session/ArraySessionHandler.php new file mode 100644 index 000000000..f8af8104b --- /dev/null +++ b/vendor/php-mcp/server/src/Session/ArraySessionHandler.php @@ -0,0 +1,77 @@ + + */ + protected array $store = []; + + private ClockInterface $clock; + + public function __construct( + public readonly int $ttl = 3600, + ?ClockInterface $clock = null + ) { + $this->clock = $clock ?? new SystemClock(); + } + + public function read(string $sessionId): string|false + { + $session = $this->store[$sessionId] ?? ''; + if ($session === '') { + return false; + } + + $currentTimestamp = $this->clock->now()->getTimestamp(); + + if ($currentTimestamp - $session['timestamp'] > $this->ttl) { + unset($this->store[$sessionId]); + return false; + } + + return $session['data']; + } + + public function write(string $sessionId, string $data): bool + { + $this->store[$sessionId] = [ + 'data' => $data, + 'timestamp' => $this->clock->now()->getTimestamp(), + ]; + + return true; + } + + public function destroy(string $sessionId): bool + { + if (isset($this->store[$sessionId])) { + unset($this->store[$sessionId]); + } + + return true; + } + + public function gc(int $maxLifetime): array + { + $currentTimestamp = $this->clock->now()->getTimestamp(); + $deletedSessions = []; + + foreach ($this->store as $sessionId => $session) { + if ($currentTimestamp - $session['timestamp'] > $maxLifetime) { + unset($this->store[$sessionId]); + $deletedSessions[] = $sessionId; + } + } + + return $deletedSessions; + } +} diff --git a/vendor/php-mcp/server/src/Session/CacheSessionHandler.php b/vendor/php-mcp/server/src/Session/CacheSessionHandler.php new file mode 100644 index 000000000..e4f1f7c3e --- /dev/null +++ b/vendor/php-mcp/server/src/Session/CacheSessionHandler.php @@ -0,0 +1,83 @@ +sessionIndex = $this->cache->get(self::SESSION_INDEX_KEY, []); + $this->clock = $clock ?? new SystemClock(); + } + + public function read(string $sessionId): string|false + { + $session = $this->cache->get($sessionId, false); + if ($session === false) { + if (isset($this->sessionIndex[$sessionId])) { + unset($this->sessionIndex[$sessionId]); + $this->cache->set(self::SESSION_INDEX_KEY, $this->sessionIndex); + } + return false; + } + + if (!isset($this->sessionIndex[$sessionId])) { + $this->sessionIndex[$sessionId] = $this->clock->now()->getTimestamp(); + $this->cache->set(self::SESSION_INDEX_KEY, $this->sessionIndex); + return $session; + } + + if ($this->clock->now()->getTimestamp() - $this->sessionIndex[$sessionId] > $this->ttl) { + $this->cache->delete($sessionId); + return false; + } + + return $session; + } + + public function write(string $sessionId, string $data): bool + { + $this->sessionIndex[$sessionId] = $this->clock->now()->getTimestamp(); + $this->cache->set(self::SESSION_INDEX_KEY, $this->sessionIndex); + return $this->cache->set($sessionId, $data); + } + + public function destroy(string $sessionId): bool + { + unset($this->sessionIndex[$sessionId]); + $this->cache->set(self::SESSION_INDEX_KEY, $this->sessionIndex); + return $this->cache->delete($sessionId); + } + + public function gc(int $maxLifetime): array + { + $currentTime = $this->clock->now()->getTimestamp(); + $deletedSessions = []; + + foreach ($this->sessionIndex as $sessionId => $timestamp) { + if ($currentTime - $timestamp > $maxLifetime) { + $this->cache->delete($sessionId); + unset($this->sessionIndex[$sessionId]); + $deletedSessions[] = $sessionId; + } + } + + $this->cache->set(self::SESSION_INDEX_KEY, $this->sessionIndex); + + return $deletedSessions; + } +} diff --git a/vendor/php-mcp/server/src/Session/Session.php b/vendor/php-mcp/server/src/Session/Session.php new file mode 100644 index 000000000..3d4283edf --- /dev/null +++ b/vendor/php-mcp/server/src/Session/Session.php @@ -0,0 +1,206 @@ + Stores all session data. + * Keys are snake_case by convention for MCP-specific data. + * + * Official keys are: + * - initialized: bool + * - client_info: array|null + * - protocol_version: string|null + * - subscriptions: array + * - message_queue: array + * - log_level: string|null + */ + protected array $data = []; + + public function __construct( + protected SessionHandlerInterface $handler, + protected string $id = '', + ?array $data = null + ) { + if (empty($this->id)) { + $this->id = $this->generateId(); + } + + if ($data !== null) { + $this->hydrate($data); + } elseif ($sessionData = $this->handler->read($this->id)) { + $this->data = json_decode($sessionData, true) ?? []; + } + } + + /** + * Retrieve an existing session instance from handler or return null if session doesn't exist + */ + public static function retrieve(string $id, SessionHandlerInterface $handler): ?SessionInterface + { + $sessionData = $handler->read($id); + + if (!$sessionData) { + return null; + } + + $data = json_decode($sessionData, true); + if ($data === null) { + return null; + } + + return new static($handler, $id, $data); + } + + public function getId(): string + { + return $this->id; + } + + public function getHandler(): SessionHandlerInterface + { + return $this->handler; + } + + public function generateId(): string + { + return bin2hex(random_bytes(16)); + } + + public function save(): void + { + $this->handler->write($this->id, json_encode($this->data)); + } + + public function get(string $key, mixed $default = null): mixed + { + $key = explode('.', $key); + $data = $this->data; + + foreach ($key as $segment) { + if (is_array($data) && array_key_exists($segment, $data)) { + $data = $data[$segment]; + } else { + return $default; + } + } + + return $data; + } + + public function set(string $key, mixed $value, bool $overwrite = true): void + { + $segments = explode('.', $key); + $data = &$this->data; + + while (count($segments) > 1) { + $segment = array_shift($segments); + if (!isset($data[$segment]) || !is_array($data[$segment])) { + $data[$segment] = []; + } + $data = &$data[$segment]; + } + + $lastKey = array_shift($segments); + if ($overwrite || !isset($data[$lastKey])) { + $data[$lastKey] = $value; + } + } + + public function has(string $key): bool + { + $key = explode('.', $key); + $data = $this->data; + + foreach ($key as $segment) { + if (is_array($data) && array_key_exists($segment, $data)) { + $data = $data[$segment]; + } elseif (is_object($data) && isset($data->{$segment})) { + $data = $data->{$segment}; + } else { + return false; + } + } + + return true; + } + + public function forget(string $key): void + { + $segments = explode('.', $key); + $data = &$this->data; + + while (count($segments) > 1) { + $segment = array_shift($segments); + if (!isset($data[$segment]) || !is_array($data[$segment])) { + $data[$segment] = []; + } + $data = &$data[$segment]; + } + + $lastKey = array_shift($segments); + if (isset($data[$lastKey])) { + unset($data[$lastKey]); + } + } + + public function clear(): void + { + $this->data = []; + } + + public function pull(string $key, mixed $default = null): mixed + { + $value = $this->get($key, $default); + $this->forget($key); + return $value; + } + + public function all(): array + { + return $this->data; + } + + public function hydrate(array $attributes): void + { + $this->data = array_merge( + [ + 'initialized' => false, + 'client_info' => null, + 'protocol_version' => null, + 'message_queue' => [], + 'log_level' => null, + ], + $attributes + ); + unset($this->data['id']); + } + + public function queueMessage(string $rawFramedMessage): void + { + $this->data['message_queue'][] = $rawFramedMessage; + } + + public function dequeueMessages(): array + { + $messages = $this->data['message_queue'] ?? []; + $this->data['message_queue'] = []; + return $messages; + } + + public function hasQueuedMessages(): bool + { + return !empty($this->data['message_queue']); + } + + public function jsonSerialize(): array + { + return $this->all(); + } +} diff --git a/vendor/php-mcp/server/src/Session/SessionManager.php b/vendor/php-mcp/server/src/Session/SessionManager.php new file mode 100644 index 000000000..01d5edd2c --- /dev/null +++ b/vendor/php-mcp/server/src/Session/SessionManager.php @@ -0,0 +1,159 @@ +loop ??= Loop::get(); + } + + /** + * Start the garbage collection timer + */ + public function startGcTimer(): void + { + if ($this->gcTimer !== null) { + return; + } + + $this->gcTimer = $this->loop->addPeriodicTimer($this->gcInterval, [$this, 'gc']); + } + + public function gc(): array + { + $deletedSessions = $this->handler->gc($this->ttl); + + foreach ($deletedSessions as $sessionId) { + $this->emit('session_deleted', [$sessionId]); + } + + if (count($deletedSessions) > 0) { + $this->logger->debug('Session garbage collection complete', [ + 'purged_sessions' => count($deletedSessions), + ]); + } + + return $deletedSessions; + } + + /** + * Stop the garbage collection timer + */ + public function stopGcTimer(): void + { + if ($this->gcTimer !== null) { + $this->loop->cancelTimer($this->gcTimer); + $this->gcTimer = null; + } + } + + /** + * Create a new session + */ + public function createSession(string $sessionId): SessionInterface + { + $session = new Session($this->handler, $sessionId); + + $session->hydrate([ + 'initialized' => false, + 'client_info' => null, + 'protocol_version' => null, + 'subscriptions' => [], // [uri => true] + 'message_queue' => [], // string[] (raw JSON-RPC frames) + 'log_level' => null, + ]); + + $session->save(); + + $this->logger->info('Session created', ['sessionId' => $sessionId]); + $this->emit('session_created', [$sessionId, $session]); + + return $session; + } + + /** + * Get an existing session + */ + public function getSession(string $sessionId): ?SessionInterface + { + return Session::retrieve($sessionId, $this->handler); + } + + public function hasSession(string $sessionId): bool + { + return $this->getSession($sessionId) !== null; + } + + /** + * Delete a session completely + */ + public function deleteSession(string $sessionId): bool + { + $success = $this->handler->destroy($sessionId); + + if ($success) { + $this->emit('session_deleted', [$sessionId]); + $this->logger->info('Session deleted', ['sessionId' => $sessionId]); + } else { + $this->logger->warning('Failed to delete session', ['sessionId' => $sessionId]); + } + + return $success; + } + + public function queueMessage(string $sessionId, string $message): void + { + $session = $this->getSession($sessionId); + if ($session === null) { + return; + } + + $session->queueMessage($message); + $session->save(); + } + + public function dequeueMessages(string $sessionId): array + { + $session = $this->getSession($sessionId); + if ($session === null) { + return []; + } + + $messages = $session->dequeueMessages(); + $session->save(); + + return $messages; + } + + public function hasQueuedMessages(string $sessionId): bool + { + $session = $this->getSession($sessionId, true); + if ($session === null) { + return false; + } + + return $session->hasQueuedMessages(); + } +} diff --git a/vendor/php-mcp/server/src/Session/SubscriptionManager.php b/vendor/php-mcp/server/src/Session/SubscriptionManager.php new file mode 100644 index 000000000..e7fc7338b --- /dev/null +++ b/vendor/php-mcp/server/src/Session/SubscriptionManager.php @@ -0,0 +1,96 @@ +> Key: URI, Value: array of session IDs */ + private array $resourceSubscribers = []; + + /** @var array> Key: Session ID, Value: array of URIs */ + private array $sessionSubscriptions = []; + + public function __construct( + private readonly LoggerInterface $logger + ) { + } + + /** + * Subscribe a session to a resource + */ + public function subscribe(string $sessionId, string $uri): void + { + // Add to both mappings for efficient lookup + $this->resourceSubscribers[$uri][$sessionId] = true; + $this->sessionSubscriptions[$sessionId][$uri] = true; + + $this->logger->debug('Session subscribed to resource', [ + 'sessionId' => $sessionId, + 'uri' => $uri + ]); + } + + /** + * Unsubscribe a session from a resource + */ + public function unsubscribe(string $sessionId, string $uri): void + { + unset($this->resourceSubscribers[$uri][$sessionId]); + unset($this->sessionSubscriptions[$sessionId][$uri]); + + // Clean up empty arrays + if (empty($this->resourceSubscribers[$uri])) { + unset($this->resourceSubscribers[$uri]); + } + + $this->logger->debug('Session unsubscribed from resource', [ + 'sessionId' => $sessionId, + 'uri' => $uri + ]); + } + + /** + * Get all sessions subscribed to a resource + */ + public function getSubscribers(string $uri): array + { + return array_keys($this->resourceSubscribers[$uri] ?? []); + } + + /** + * Check if a session is subscribed to a resource + */ + public function isSubscribed(string $sessionId, string $uri): bool + { + return isset($this->sessionSubscriptions[$sessionId][$uri]); + } + + /** + * Clean up all subscriptions for a session + */ + public function cleanupSession(string $sessionId): void + { + if (!isset($this->sessionSubscriptions[$sessionId])) { + return; + } + + $uris = array_keys($this->sessionSubscriptions[$sessionId]); + foreach ($uris as $uri) { + unset($this->resourceSubscribers[$uri][$sessionId]); + + // Clean up empty arrays + if (empty($this->resourceSubscribers[$uri])) { + unset($this->resourceSubscribers[$uri]); + } + } + + unset($this->sessionSubscriptions[$sessionId]); + + $this->logger->debug('Cleaned up all subscriptions for session', [ + 'sessionId' => $sessionId, + 'count' => count($uris) + ]); + } +} diff --git a/vendor/php-mcp/server/src/Transports/HttpServerTransport.php b/vendor/php-mcp/server/src/Transports/HttpServerTransport.php new file mode 100644 index 000000000..196570403 --- /dev/null +++ b/vendor/php-mcp/server/src/Transports/HttpServerTransport.php @@ -0,0 +1,357 @@ + sessionId => SSE Stream */ + private array $activeSseStreams = []; + + protected bool $listening = false; + + protected bool $closing = false; + + protected string $ssePath; + + protected string $messagePath; + + /** + * @param string $host Host to bind to (e.g., '127.0.0.1', '0.0.0.0'). + * @param int $port Port to listen on (e.g., 8080). + * @param string $mcpPathPrefix URL prefix for MCP endpoints (e.g., 'mcp'). + * @param array|null $sslContext Optional SSL context options for React SocketServer (for HTTPS). + */ + public function __construct( + private readonly string $host = '127.0.0.1', + private readonly int $port = 8080, + private readonly string $mcpPathPrefix = 'mcp', + private readonly ?array $sslContext = null, + ) { + $this->logger = new NullLogger(); + $this->loop = Loop::get(); + $this->ssePath = '/' . trim($mcpPathPrefix, '/') . '/sse'; + $this->messagePath = '/' . trim($mcpPathPrefix, '/') . '/message'; + } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } + + public function setLoop(LoopInterface $loop): void + { + $this->loop = $loop; + } + + protected function generateId(): string + { + return bin2hex(random_bytes(16)); // 32 hex characters + } + + /** + * Starts the HTTP server listener. + * + * @throws TransportException If port binding fails. + */ + public function listen(): void + { + if ($this->listening) { + throw new TransportException('Http transport is already listening.'); + } + if ($this->closing) { + throw new TransportException('Cannot listen, transport is closing/closed.'); + } + + $listenAddress = "{$this->host}:{$this->port}"; + $protocol = $this->sslContext ? 'https' : 'http'; + + try { + $this->socket = new SocketServer( + $listenAddress, + $this->sslContext ?? [], + $this->loop + ); + + $this->http = new HttpServer($this->loop, $this->createRequestHandler()); + $this->http->listen($this->socket); + + $this->socket->on('error', function (Throwable $error) { + $this->logger->error('Socket server error.', ['error' => $error->getMessage()]); + $this->emit('error', [new TransportException("Socket server error: {$error->getMessage()}", 0, $error)]); + $this->close(); + }); + + $this->logger->info("Server is up and listening on {$protocol}://{$listenAddress} 🚀"); + $this->logger->info("SSE Endpoint: {$protocol}://{$listenAddress}{$this->ssePath}"); + $this->logger->info("Message Endpoint: {$protocol}://{$listenAddress}{$this->messagePath}"); + + $this->listening = true; + $this->closing = false; + $this->emit('ready'); + } catch (Throwable $e) { + $this->logger->error("Failed to start listener on {$listenAddress}", ['exception' => $e]); + throw new TransportException("Failed to start HTTP listener on {$listenAddress}: {$e->getMessage()}", 0, $e); + } + } + + /** Creates the main request handling callback for ReactPHP HttpServer */ + protected function createRequestHandler(): callable + { + return function (ServerRequestInterface $request) { + $path = $request->getUri()->getPath(); + $method = $request->getMethod(); + $this->logger->debug('Received request', ['method' => $method, 'path' => $path]); + + if ($method === 'GET' && $path === $this->ssePath) { + return $this->handleSseRequest($request); + } + + if ($method === 'POST' && $path === $this->messagePath) { + return $this->handleMessagePostRequest($request); + } + + $this->logger->debug('404 Not Found', ['method' => $method, 'path' => $path]); + + return new Response(404, ['Content-Type' => 'text/plain'], 'Not Found'); + }; + } + + /** Handles a new SSE connection request */ + protected function handleSseRequest(ServerRequestInterface $request): Response + { + $sessionId = $this->generateId(); + $this->logger->info('New SSE connection', ['sessionId' => $sessionId]); + + $sseStream = new ThroughStream(); + + $sseStream->on('close', function () use ($sessionId) { + $this->logger->info('SSE stream closed', ['sessionId' => $sessionId]); + unset($this->activeSseStreams[$sessionId]); + $this->emit('client_disconnected', [$sessionId, 'SSE stream closed']); + }); + + $sseStream->on('error', function (Throwable $error) use ($sessionId) { + $this->logger->warning('SSE stream error', ['sessionId' => $sessionId, 'error' => $error->getMessage()]); + unset($this->activeSseStreams[$sessionId]); + $this->emit('error', [new TransportException("SSE Stream Error: {$error->getMessage()}", 0, $error), $sessionId]); + $this->emit('client_disconnected', [$sessionId, 'SSE stream error']); + }); + + $this->activeSseStreams[$sessionId] = $sseStream; + + $this->loop->futureTick(function () use ($sessionId, $request, $sseStream) { + if (! isset($this->activeSseStreams[$sessionId]) || ! $sseStream->isWritable()) { + $this->logger->warning('Cannot send initial endpoint event, stream closed/invalid early.', ['sessionId' => $sessionId]); + + return; + } + + try { + $baseUri = $request->getUri()->withPath($this->messagePath)->withQuery('')->withFragment(''); + $postEndpointWithId = (string) $baseUri->withQuery("clientId={$sessionId}"); + $this->sendSseEvent($sseStream, 'endpoint', $postEndpointWithId, "init-{$sessionId}"); + + $this->emit('client_connected', [$sessionId]); + } catch (Throwable $e) { + $this->logger->error('Error sending initial endpoint event', ['sessionId' => $sessionId, 'exception' => $e]); + $sseStream->close(); + } + }); + + return new Response( + 200, + [ + 'Content-Type' => 'text/event-stream', + 'Cache-Control' => 'no-cache', + 'Connection' => 'keep-alive', + 'X-Accel-Buffering' => 'no', + 'Access-Control-Allow-Origin' => '*', + ], + $sseStream + ); + } + + /** Handles incoming POST requests with messages */ + protected function handleMessagePostRequest(ServerRequestInterface $request): Response + { + $queryParams = $request->getQueryParams(); + $sessionId = $queryParams['clientId'] ?? null; + $jsonEncodeFlags = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE; + + if (! $sessionId || ! is_string($sessionId)) { + $this->logger->warning('Received POST without valid clientId query parameter.'); + $error = Error::forInvalidRequest('Missing or invalid clientId query parameter'); + + return new Response(400, ['Content-Type' => 'application/json'], json_encode($error, $jsonEncodeFlags)); + } + + if (! isset($this->activeSseStreams[$sessionId])) { + $this->logger->warning('Received POST for unknown or disconnected sessionId.', ['sessionId' => $sessionId]); + + $error = Error::forInvalidRequest('Session ID not found or disconnected'); + + return new Response(404, ['Content-Type' => 'application/json'], json_encode($error, $jsonEncodeFlags)); + } + + if (! str_contains(strtolower($request->getHeaderLine('Content-Type')), 'application/json')) { + $error = Error::forInvalidRequest('Content-Type must be application/json'); + + return new Response(415, ['Content-Type' => 'application/json'], json_encode($error, $jsonEncodeFlags)); + } + + $body = $request->getBody()->getContents(); + + if (empty($body)) { + $this->logger->warning('Received empty POST body', ['sessionId' => $sessionId]); + + $error = Error::forInvalidRequest('Empty request body'); + + return new Response(400, ['Content-Type' => 'application/json'], json_encode($error, $jsonEncodeFlags)); + } + + try { + $message = Parser::parse($body); + } catch (Throwable $e) { + $this->logger->error('Error parsing message', ['sessionId' => $sessionId, 'exception' => $e]); + + $error = Error::forParseError('Invalid JSON-RPC message: ' . $e->getMessage()); + + return new Response(400, ['Content-Type' => 'application/json'], json_encode($error, $jsonEncodeFlags)); + } + + $this->emit('message', [$message, $sessionId]); + + return new Response(202, ['Content-Type' => 'text/plain'], 'Accepted'); + } + + + /** + * Sends a raw JSON-RPC message frame to a specific client via SSE. + */ + public function sendMessage(Message $message, string $sessionId, array $context = []): PromiseInterface + { + if (! isset($this->activeSseStreams[$sessionId])) { + return reject(new TransportException("Cannot send message: Client '{$sessionId}' not connected via SSE.")); + } + + $stream = $this->activeSseStreams[$sessionId]; + if (! $stream->isWritable()) { + return reject(new TransportException("Cannot send message: SSE stream for client '{$sessionId}' is not writable.")); + } + + $json = json_encode($message, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + + if ($json === '') { + return resolve(null); + } + + $deferred = new Deferred(); + $written = $this->sendSseEvent($stream, 'message', $json); + + if ($written) { + $deferred->resolve(null); + } else { + $this->logger->debug('SSE stream buffer full, waiting for drain.', ['sessionId' => $sessionId]); + $stream->once('drain', function () use ($deferred, $sessionId) { + $this->logger->debug('SSE stream drained.', ['sessionId' => $sessionId]); + $deferred->resolve(null); + }); + } + + return $deferred->promise(); + } + + /** Helper to format and write an SSE event */ + private function sendSseEvent(WritableStreamInterface $stream, string $event, string $data, ?string $id = null): bool + { + if (! $stream->isWritable()) { + return false; + } + + $frame = "event: {$event}\n"; + if ($id !== null) { + $frame .= "id: {$id}\n"; + } + + $lines = explode("\n", $data); + foreach ($lines as $line) { + $frame .= "data: {$line}\n"; + } + $frame .= "\n"; // End of event + + $this->logger->debug('Sending SSE event', ['event' => $event, 'frame' => $frame]); + + return $stream->write($frame); + } + + /** + * Stops the HTTP server and closes all connections. + */ + public function close(): void + { + if ($this->closing) { + return; + } + $this->closing = true; + $this->listening = false; + $this->logger->info('Closing transport...'); + + if ($this->socket) { + $this->socket->close(); + $this->socket = null; + } + + $activeStreams = $this->activeSseStreams; + $this->activeSseStreams = []; + foreach ($activeStreams as $sessionId => $stream) { + $this->logger->debug('Closing active SSE stream', ['sessionId' => $sessionId]); + unset($this->activeSseStreams[$sessionId]); + $stream->close(); + } + + $this->emit('close', ['HttpTransport closed.']); + $this->removeAllListeners(); + } +} diff --git a/vendor/php-mcp/server/src/Transports/StdioServerTransport.php b/vendor/php-mcp/server/src/Transports/StdioServerTransport.php new file mode 100644 index 000000000..4535c174f --- /dev/null +++ b/vendor/php-mcp/server/src/Transports/StdioServerTransport.php @@ -0,0 +1,243 @@ +inputStreamResource === STDIN && $this->outputStreamResource === STDOUT)) { + $message = 'STDIN and STDOUT are not supported as input and output stream resources' . + 'on Windows due to PHP\'s limitations with non blocking pipes.' . + 'Please use WSL or HttpServerTransport, or if you are advanced, provide your own stream resources.'; + + throw new TransportException($message); + } + + // if (str_contains(PHP_OS, 'WIN')) { + // $this->inputStreamResource = pclose(popen('winpty -c "'.$this->inputStreamResource.'"', 'r')); + // $this->outputStreamResource = pclose(popen('winpty -c "'.$this->outputStreamResource.'"', 'w')); + // } + + if (! is_resource($this->inputStreamResource) || get_resource_type($this->inputStreamResource) !== 'stream') { + throw new TransportException('Invalid input stream resource provided.'); + } + + if (! is_resource($this->outputStreamResource) || get_resource_type($this->outputStreamResource) !== 'stream') { + throw new TransportException('Invalid output stream resource provided.'); + } + + $this->logger = new NullLogger(); + $this->loop = Loop::get(); + } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } + + public function setLoop(LoopInterface $loop): void + { + $this->loop = $loop; + } + + /** + * Starts listening on STDIN. + * + * @throws TransportException If already listening or streams cannot be opened. + */ + public function listen(): void + { + if ($this->listening) { + throw new TransportException('Stdio transport is already listening.'); + } + + if ($this->closing) { + throw new TransportException('Cannot listen, transport is closing/closed.'); + } + + try { + $this->stdin = new ReadableResourceStream($this->inputStreamResource, $this->loop); + $this->stdout = new WritableResourceStream($this->outputStreamResource, $this->loop); + } catch (Throwable $e) { + $this->logger->error('Failed to open STDIN/STDOUT streams.', ['exception' => $e]); + throw new TransportException("Failed to open standard streams: {$e->getMessage()}", 0, $e); + } + + $this->stdin->on('data', function ($chunk) { + $this->buffer .= $chunk; + $this->processBuffer(); + }); + + $this->stdin->on('error', function (Throwable $error) { + $this->logger->error('STDIN stream error.', ['error' => $error->getMessage()]); + $this->emit('error', [new TransportException("STDIN error: {$error->getMessage()}", 0, $error), self::CLIENT_ID]); + $this->close(); + }); + + $this->stdin->on('close', function () { + $this->logger->info('STDIN stream closed.'); + $this->emit('client_disconnected', [self::CLIENT_ID, 'STDIN Closed']); + $this->close(); + }); + + $this->stdout->on('error', function (Throwable $error) { + $this->logger->error('STDOUT stream error.', ['error' => $error->getMessage()]); + $this->emit('error', [new TransportException("STDOUT error: {$error->getMessage()}", 0, $error), self::CLIENT_ID]); + $this->close(); + }); + + try { + $signalHandler = function (int $signal) { + $this->logger->info("Received signal {$signal}, shutting down."); + $this->close(); + }; + $this->loop->addSignal(SIGTERM, $signalHandler); + $this->loop->addSignal(SIGINT, $signalHandler); + } catch (Throwable $e) { + $this->logger->debug('Signal handling not supported by current event loop.'); + } + + $this->logger->info('Server is up and listening on STDIN 🚀'); + + $this->listening = true; + $this->closing = false; + $this->emit('ready'); + $this->emit('client_connected', [self::CLIENT_ID]); + } + + /** Processes the internal buffer to find complete lines/frames. */ + private function processBuffer(): void + { + while (str_contains($this->buffer, "\n")) { + $pos = strpos($this->buffer, "\n"); + $line = substr($this->buffer, 0, $pos); + $this->buffer = substr($this->buffer, $pos + 1); + + $trimmedLine = trim($line); + if (empty($trimmedLine)) { + continue; + } + + try { + $message = Parser::parse($trimmedLine); + } catch (Throwable $e) { + $this->logger->error('Error parsing message', ['exception' => $e]); + $error = Error::forParseError("Invalid JSON: " . $e->getMessage()); + $this->sendMessage($error, self::CLIENT_ID); + continue; + } + + $this->emit('message', [$message, self::CLIENT_ID]); + } + } + + /** + * Sends a raw, framed message to STDOUT. + */ + public function sendMessage(Message $message, string $sessionId, array $context = []): PromiseInterface + { + if ($this->closing || ! $this->stdout || ! $this->stdout->isWritable()) { + return reject(new TransportException('Stdio transport is closed or STDOUT is not writable.')); + } + + $deferred = new Deferred(); + $json = json_encode($message, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + $written = $this->stdout->write($json . "\n"); + + if ($written) { + $deferred->resolve(null); + } else { + $this->logger->debug('STDOUT buffer full, waiting for drain.'); + $this->stdout->once('drain', function () use ($deferred) { + $this->logger->debug('STDOUT drained.'); + $deferred->resolve(null); + }); + } + + return $deferred->promise(); + } + + /** + * Stops listening and cleans up resources. + */ + public function close(): void + { + if ($this->closing) { + return; + } + $this->closing = true; + $this->listening = false; + $this->logger->info('Closing Stdio transport...'); + + $this->stdin?->close(); + $this->stdout?->close(); + + $this->stdin = null; + $this->stdout = null; + + $this->emit('close', ['StdioTransport closed.']); + $this->removeAllListeners(); + } +} diff --git a/vendor/php-mcp/server/src/Transports/StreamableHttpServerTransport.php b/vendor/php-mcp/server/src/Transports/StreamableHttpServerTransport.php new file mode 100644 index 000000000..ae29267e7 --- /dev/null +++ b/vendor/php-mcp/server/src/Transports/StreamableHttpServerTransport.php @@ -0,0 +1,624 @@ + + */ + private array $pendingRequests = []; + + /** + * Stores active SSE streams. + * Key: streamId + * Value: ['stream' => ThroughStream, 'sessionId' => string, 'context' => array] + * @var array + */ + private array $activeSseStreams = []; + + private ?ThroughStream $getStream = null; + + /** + * @param bool $enableJsonResponse If true, the server will return JSON responses instead of starting an SSE stream. + * This can be useful for simple request/response scenarios without streaming. + */ + public function __construct( + private readonly string $host = '127.0.0.1', + private readonly int $port = 8080, + private string $mcpPath = '/mcp', + private ?array $sslContext = null, + private readonly bool $enableJsonResponse = true, + private readonly bool $stateless = false, + ?EventStoreInterface $eventStore = null + ) { + $this->logger = new NullLogger(); + $this->loop = Loop::get(); + $this->mcpPath = '/' . trim($mcpPath, '/'); + $this->eventStore = $eventStore; + } + + protected function generateId(): string + { + return bin2hex(random_bytes(16)); // 32 hex characters + } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } + + public function setLoop(LoopInterface $loop): void + { + $this->loop = $loop; + } + + public function listen(): void + { + if ($this->listening) { + throw new TransportException('StreamableHttp transport is already listening.'); + } + + if ($this->closing) { + throw new TransportException('Cannot listen, transport is closing/closed.'); + } + + $listenAddress = "{$this->host}:{$this->port}"; + $protocol = $this->sslContext ? 'https' : 'http'; + + try { + $this->socket = new SocketServer( + $listenAddress, + $this->sslContext ?? [], + $this->loop + ); + + $this->http = new HttpServer($this->loop, $this->createRequestHandler()); + $this->http->listen($this->socket); + + $this->socket->on('error', function (Throwable $error) { + $this->logger->error('Socket server error (StreamableHttp).', ['error' => $error->getMessage()]); + $this->emit('error', [new TransportException("Socket server error: {$error->getMessage()}", 0, $error)]); + $this->close(); + }); + + $this->logger->info("Server is up and listening on {$protocol}://{$listenAddress} 🚀"); + $this->logger->info("MCP Endpoint: {$protocol}://{$listenAddress}{$this->mcpPath}"); + + $this->listening = true; + $this->closing = false; + $this->emit('ready'); + } catch (Throwable $e) { + $this->logger->error("Failed to start StreamableHttp listener on {$listenAddress}", ['exception' => $e]); + throw new TransportException("Failed to start StreamableHttp listener on {$listenAddress}: {$e->getMessage()}", 0, $e); + } + } + + private function createRequestHandler(): callable + { + return function (ServerRequestInterface $request) { + $path = $request->getUri()->getPath(); + $method = $request->getMethod(); + + $this->logger->debug("Request received", ['method' => $method, 'path' => $path, 'target' => $this->mcpPath]); + + if ($path !== $this->mcpPath) { + $error = Error::forInvalidRequest("Not found: {$path}"); + return new HttpResponse(404, ['Content-Type' => 'application/json'], json_encode($error)); + } + + $corsHeaders = [ + 'Access-Control-Allow-Origin' => '*', + 'Access-Control-Allow-Methods' => 'GET, POST, DELETE, OPTIONS', + 'Access-Control-Allow-Headers' => 'Content-Type, Mcp-Session-Id, Last-Event-ID, Authorization', + ]; + + if ($method === 'OPTIONS') { + return new HttpResponse(204, $corsHeaders); + } + + $addCors = function (HttpResponse $r) use ($corsHeaders) { + foreach ($corsHeaders as $key => $value) { + $r = $r->withAddedHeader($key, $value); + } + return $r; + }; + + try { + return match ($method) { + 'GET' => $this->handleGetRequest($request)->then($addCors, fn($e) => $addCors($this->handleRequestError($e, $request))), + 'POST' => $this->handlePostRequest($request)->then($addCors, fn($e) => $addCors($this->handleRequestError($e, $request))), + 'DELETE' => $this->handleDeleteRequest($request)->then($addCors, fn($e) => $addCors($this->handleRequestError($e, $request))), + default => $addCors($this->handleUnsupportedRequest($request)), + }; + } catch (Throwable $e) { + return $addCors($this->handleRequestError($e, $request)); + } + }; + } + + private function handleGetRequest(ServerRequestInterface $request): PromiseInterface + { + if ($this->stateless) { + $error = Error::forInvalidRequest("GET requests (SSE streaming) are not supported in stateless mode."); + return resolve(new HttpResponse(405, ['Content-Type' => 'application/json'], json_encode($error))); + } + + $acceptHeader = $request->getHeaderLine('Accept'); + if (!str_contains($acceptHeader, 'text/event-stream')) { + $error = Error::forInvalidRequest("Not Acceptable: Client must accept text/event-stream for GET requests."); + return resolve(new HttpResponse(406, ['Content-Type' => 'application/json'], json_encode($error))); + } + + $sessionId = $request->getHeaderLine('Mcp-Session-Id'); + if (empty($sessionId)) { + $this->logger->warning("GET request without Mcp-Session-Id."); + $error = Error::forInvalidRequest("Mcp-Session-Id header required for GET requests."); + return resolve(new HttpResponse(400, ['Content-Type' => 'application/json'], json_encode($error))); + } + + $this->getStream = new ThroughStream(); + + $this->getStream->on('close', function () use ($sessionId) { + $this->logger->debug("GET SSE stream closed.", ['sessionId' => $sessionId]); + $this->getStream = null; + }); + + $this->getStream->on('error', function (Throwable $e) use ($sessionId) { + $this->logger->error("GET SSE stream error.", ['sessionId' => $sessionId, 'error' => $e->getMessage()]); + $this->getStream = null; + }); + + $headers = [ + 'Content-Type' => 'text/event-stream', + 'Cache-Control' => 'no-cache', + 'Connection' => 'keep-alive', + 'X-Accel-Buffering' => 'no', + ]; + + $response = new HttpResponse(200, $headers, $this->getStream); + + if ($this->eventStore) { + $lastEventId = $request->getHeaderLine('Last-Event-ID'); + $this->replayEvents($lastEventId, $this->getStream, $sessionId); + } + + return resolve($response); + } + + private function handlePostRequest(ServerRequestInterface $request): PromiseInterface + { + $deferred = new Deferred(); + + $acceptHeader = $request->getHeaderLine('Accept'); + if (!str_contains($acceptHeader, 'application/json') && !str_contains($acceptHeader, 'text/event-stream')) { + $error = Error::forInvalidRequest("Not Acceptable: Client must accept both application/json or text/event-stream"); + $deferred->resolve(new HttpResponse(406, ['Content-Type' => 'application/json'], json_encode($error))); + return $deferred->promise(); + } + + if (!str_contains($request->getHeaderLine('Content-Type'), 'application/json')) { + $error = Error::forInvalidRequest("Unsupported Media Type: Content-Type must be application/json"); + $deferred->resolve(new HttpResponse(415, ['Content-Type' => 'application/json'], json_encode($error))); + return $deferred->promise(); + } + + $body = $request->getBody()->getContents(); + + if (empty($body)) { + $this->logger->warning("Received empty POST body"); + $error = Error::forInvalidRequest("Empty request body."); + $deferred->resolve(new HttpResponse(400, ['Content-Type' => 'application/json'], json_encode($error))); + return $deferred->promise(); + } + + try { + $message = Parser::parse($body); + } catch (Throwable $e) { + $this->logger->error("Failed to parse MCP message from POST body", ['error' => $e->getMessage()]); + $error = Error::forParseError("Invalid JSON: " . $e->getMessage()); + $deferred->resolve(new HttpResponse(400, ['Content-Type' => 'application/json'], json_encode($error))); + return $deferred->promise(); + } + + $isInitializeRequest = ($message instanceof Request && $message->method === 'initialize'); + $sessionId = null; + + if ($this->stateless) { + $sessionId = $this->generateId(); + $this->emit('client_connected', [$sessionId]); + } else { + if ($isInitializeRequest) { + if ($request->hasHeader('Mcp-Session-Id')) { + $this->logger->warning("Client sent Mcp-Session-Id with InitializeRequest. Ignoring.", ['clientSentId' => $request->getHeaderLine('Mcp-Session-Id')]); + $error = Error::forInvalidRequest("Invalid request: Session already initialized. Mcp-Session-Id header not allowed with InitializeRequest.", $message->getId()); + $deferred->resolve(new HttpResponse(400, ['Content-Type' => 'application/json'], json_encode($error))); + return $deferred->promise(); + } + + $sessionId = $this->generateId(); + $this->emit('client_connected', [$sessionId]); + } else { + $sessionId = $request->getHeaderLine('Mcp-Session-Id'); + + if (empty($sessionId)) { + $this->logger->warning("POST request without Mcp-Session-Id."); + $error = Error::forInvalidRequest("Mcp-Session-Id header required for POST requests.", $message->getId()); + $deferred->resolve(new HttpResponse(400, ['Content-Type' => 'application/json'], json_encode($error))); + return $deferred->promise(); + } + } + } + + $context = [ + 'is_initialize_request' => $isInitializeRequest, + ]; + + $nRequests = match (true) { + $message instanceof Request => 1, + $message instanceof BatchRequest => $message->nRequests(), + default => 0, + }; + + if ($nRequests === 0) { + $deferred->resolve(new HttpResponse(202)); + $context['type'] = 'post_202_sent'; + } else { + if ($this->enableJsonResponse) { + $pendingRequestId = $this->generateId(); + $this->pendingRequests[$pendingRequestId] = $deferred; + + $timeoutTimer = $this->loop->addTimer(30, function () use ($pendingRequestId, $sessionId) { + if (isset($this->pendingRequests[$pendingRequestId])) { + $deferred = $this->pendingRequests[$pendingRequestId]; + unset($this->pendingRequests[$pendingRequestId]); + $this->logger->warning("Timeout waiting for direct JSON response processing.", ['pending_request_id' => $pendingRequestId, 'session_id' => $sessionId]); + $errorResponse = McpServerException::internalError("Request processing timed out.")->toJsonRpcError($pendingRequestId); + $deferred->resolve(new HttpResponse(500, ['Content-Type' => 'application/json'], json_encode($errorResponse->toArray()))); + } + }); + + $this->pendingRequests[$pendingRequestId]->promise()->finally(function () use ($timeoutTimer) { + $this->loop->cancelTimer($timeoutTimer); + }); + + $context['type'] = 'post_json'; + $context['pending_request_id'] = $pendingRequestId; + } else { + $streamId = $this->generateId(); + $sseStream = new ThroughStream(); + $this->activeSseStreams[$streamId] = [ + 'stream' => $sseStream, + 'sessionId' => $sessionId, + 'context' => ['nRequests' => $nRequests, 'nResponses' => 0] + ]; + + $sseStream->on('close', function () use ($streamId) { + $this->logger->info("POST SSE stream closed by client/server.", ['streamId' => $streamId, 'sessionId' => $this->activeSseStreams[$streamId]['sessionId']]); + unset($this->activeSseStreams[$streamId]); + }); + $sseStream->on('error', function (Throwable $e) use ($streamId) { + $this->logger->error("POST SSE stream error.", ['streamId' => $streamId, 'sessionId' => $this->activeSseStreams[$streamId]['sessionId'], 'error' => $e->getMessage()]); + unset($this->activeSseStreams[$streamId]); + }); + + $headers = [ + 'Content-Type' => 'text/event-stream', + 'Cache-Control' => 'no-cache', + 'Connection' => 'keep-alive', + 'X-Accel-Buffering' => 'no', + ]; + + if (!empty($sessionId) && !$this->stateless) { + $headers['Mcp-Session-Id'] = $sessionId; + } + + $deferred->resolve(new HttpResponse(200, $headers, $sseStream)); + $context['type'] = 'post_sse'; + $context['streamId'] = $streamId; + $context['nRequests'] = $nRequests; + } + } + + $context['stateless'] = $this->stateless; + + $this->loop->futureTick(function () use ($message, $sessionId, $context) { + $this->emit('message', [$message, $sessionId, $context]); + }); + + return $deferred->promise(); + } + + private function handleDeleteRequest(ServerRequestInterface $request): PromiseInterface + { + if ($this->stateless) { + return resolve(new HttpResponse(204)); + } + + $sessionId = $request->getHeaderLine('Mcp-Session-Id'); + if (empty($sessionId)) { + $this->logger->warning("DELETE request without Mcp-Session-Id."); + $error = Error::forInvalidRequest("Mcp-Session-Id header required for DELETE."); + return resolve(new HttpResponse(400, ['Content-Type' => 'application/json'], json_encode($error))); + } + + $streamsToClose = []; + foreach ($this->activeSseStreams as $streamId => $streamInfo) { + if ($streamInfo['sessionId'] === $sessionId) { + $streamsToClose[] = $streamId; + } + } + + foreach ($streamsToClose as $streamId) { + $this->activeSseStreams[$streamId]['stream']->end(); + unset($this->activeSseStreams[$streamId]); + } + + if ($this->getStream !== null) { + $this->getStream->end(); + $this->getStream = null; + } + + $this->emit('client_disconnected', [$sessionId, 'Session terminated by DELETE request']); + + return resolve(new HttpResponse(204)); + } + + private function handleUnsupportedRequest(ServerRequestInterface $request): HttpResponse + { + $error = Error::forInvalidRequest("Method not allowed: {$request->getMethod()}"); + $headers = [ + 'Content-Type' => 'application/json', + 'Allow' => 'GET, POST, DELETE, OPTIONS', + ]; + return new HttpResponse(405, $headers, json_encode($error)); + } + + private function handleRequestError(Throwable $e, ServerRequestInterface $request): HttpResponse + { + $this->logger->error("Error processing HTTP request", [ + 'method' => $request->getMethod(), + 'path' => $request->getUri()->getPath(), + 'exception' => $e->getMessage() + ]); + + if ($e instanceof TransportException) { + $error = Error::forInternalError("Transport Error: " . $e->getMessage()); + return new HttpResponse(500, ['Content-Type' => 'application/json'], json_encode($error)); + } + + $error = Error::forInternalError("Internal Server Error during HTTP request processing."); + return new HttpResponse(500, ['Content-Type' => 'application/json'], json_encode($error)); + } + + public function sendMessage(Message $message, string $sessionId, array $context = []): PromiseInterface + { + if ($this->closing) { + return reject(new TransportException('Transport is closing.')); + } + + $isInitializeResponse = ($context['is_initialize_request'] ?? false) && ($message instanceof Response); + + switch ($context['type'] ?? null) { + case 'post_202_sent': + return resolve(null); + + case 'post_sse': + $streamId = $context['streamId']; + if (!isset($this->activeSseStreams[$streamId])) { + $this->logger->error("SSE stream for POST not found.", ['streamId' => $streamId, 'sessionId' => $sessionId]); + return reject(new TransportException("SSE stream {$streamId} not found for POST response.")); + } + + $stream = $this->activeSseStreams[$streamId]['stream']; + if (!$stream->isWritable()) { + $this->logger->warning("SSE stream for POST is not writable.", ['streamId' => $streamId, 'sessionId' => $sessionId]); + return reject(new TransportException("SSE stream {$streamId} for POST is not writable.")); + } + + $sentCountThisCall = 0; + + if ($message instanceof Response || $message instanceof Error) { + $json = json_encode($message, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + $eventId = $this->eventStore ? $this->eventStore->storeEvent($streamId, $json) : null; + $this->sendSseEventToStream($stream, $json, $eventId); + $sentCountThisCall = 1; + } elseif ($message instanceof BatchResponse) { + foreach ($message->getAll() as $singleResponse) { + $json = json_encode($message, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + $eventId = $this->eventStore ? $this->eventStore->storeEvent($streamId, $json) : null; + $this->sendSseEventToStream($stream, $json, $eventId); + $sentCountThisCall++; + } + } + + if (isset($this->activeSseStreams[$streamId]['context'])) { + $this->activeSseStreams[$streamId]['context']['nResponses'] += $sentCountThisCall; + if ($this->activeSseStreams[$streamId]['context']['nResponses'] >= $this->activeSseStreams[$streamId]['context']['nRequests']) { + $this->logger->info("All expected responses sent for POST SSE stream. Closing.", ['streamId' => $streamId, 'sessionId' => $sessionId]); + $stream->end(); // Will trigger 'close' event. + + if ($context['stateless'] ?? false) { + $this->loop->futureTick(function () use ($sessionId) { + $this->emit('client_disconnected', [$sessionId, 'Stateless request completed']); + }); + } + } + } + + return resolve(null); + + case 'post_json': + $pendingRequestId = $context['pending_request_id']; + if (!isset($this->pendingRequests[$pendingRequestId])) { + $this->logger->error("Pending direct JSON request not found.", ['pending_request_id' => $pendingRequestId, 'session_id' => $sessionId]); + return reject(new TransportException("Pending request {$pendingRequestId} not found.")); + } + + $deferred = $this->pendingRequests[$pendingRequestId]; + unset($this->pendingRequests[$pendingRequestId]); + + $responseBody = json_encode($message, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + $headers = ['Content-Type' => 'application/json']; + if ($isInitializeResponse && !$this->stateless) { + $headers['Mcp-Session-Id'] = $sessionId; + } + + $statusCode = $context['status_code'] ?? 200; + $deferred->resolve(new HttpResponse($statusCode, $headers, $responseBody . "\n")); + + if ($context['stateless'] ?? false) { + $this->loop->futureTick(function () use ($sessionId) { + $this->emit('client_disconnected', [$sessionId, 'Stateless request completed']); + }); + } + + return resolve(null); + + default: + if ($this->getStream === null) { + $this->logger->error("GET SSE stream not found.", ['sessionId' => $sessionId]); + return reject(new TransportException("GET SSE stream not found.")); + } + + if (!$this->getStream->isWritable()) { + $this->logger->warning("GET SSE stream is not writable.", ['sessionId' => $sessionId]); + return reject(new TransportException("GET SSE stream not writable.")); + } + if ($message instanceof Response || $message instanceof Error) { + $json = json_encode($message, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + $eventId = $this->eventStore ? $this->eventStore->storeEvent('GET_STREAM', $json) : null; + $this->sendSseEventToStream($this->getStream, $json, $eventId); + } elseif ($message instanceof BatchResponse) { + foreach ($message->getAll() as $singleResponse) { + $json = json_encode($singleResponse, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + $eventId = $this->eventStore ? $this->eventStore->storeEvent('GET_STREAM', $json) : null; + $this->sendSseEventToStream($this->getStream, $json, $eventId); + } + } + return resolve(null); + } + } + + private function replayEvents(string $lastEventId, ThroughStream $sseStream, string $sessionId): void + { + if (empty($lastEventId)) { + return; + } + + try { + $this->eventStore->replayEventsAfter( + $lastEventId, + function (string $replayedEventId, string $json) use ($sseStream) { + $this->logger->debug("Replaying event", ['replayedEventId' => $replayedEventId]); + $this->sendSseEventToStream($sseStream, $json, $replayedEventId); + } + ); + } catch (Throwable $e) { + $this->logger->error("Error during event replay.", ['sessionId' => $sessionId, 'exception' => $e]); + } + } + + private function sendSseEventToStream(ThroughStream $stream, string $data, ?string $eventId = null): bool + { + if (! $stream->isWritable()) { + return false; + } + + $frame = "event: message\n"; + if ($eventId !== null) { + $frame .= "id: {$eventId}\n"; + } + + $lines = explode("\n", $data); + foreach ($lines as $line) { + $frame .= "data: {$line}\n"; + } + $frame .= "\n"; + + return $stream->write($frame); + } + + public function close(): void + { + if ($this->closing) { + return; + } + + $this->closing = true; + $this->listening = false; + $this->logger->info('Closing transport...'); + + if ($this->socket) { + $this->socket->close(); + $this->socket = null; + } + + foreach ($this->activeSseStreams as $streamId => $streamInfo) { + if ($streamInfo['stream']->isWritable()) { + $streamInfo['stream']->end(); + } + } + + if ($this->getStream !== null) { + $this->getStream->end(); + $this->getStream = null; + } + + foreach ($this->pendingRequests as $pendingRequestId => $deferred) { + $deferred->reject(new TransportException('Transport is closing.')); + } + + $this->activeSseStreams = []; + $this->pendingRequests = []; + + $this->emit('close', ['Transport closed.']); + $this->removeAllListeners(); + } +} diff --git a/vendor/php-mcp/server/src/Utils/Discoverer.php b/vendor/php-mcp/server/src/Utils/Discoverer.php new file mode 100644 index 000000000..61f3df46f --- /dev/null +++ b/vendor/php-mcp/server/src/Utils/Discoverer.php @@ -0,0 +1,393 @@ +docBlockParser = $docBlockParser ?? new DocBlockParser($this->logger); + $this->schemaGenerator = $schemaGenerator ?? new SchemaGenerator($this->docBlockParser); + } + + /** + * Discover MCP elements in the specified directories. + * + * @param string $basePath The base path for resolving directories. + * @param array $directories List of directories (relative to base path) to scan. + * @param array $excludeDirs List of directories (relative to base path) to exclude from the scan. + */ + public function discover(string $basePath, array $directories, array $excludeDirs = []): void + { + $startTime = microtime(true); + $discoveredCount = [ + 'tools' => 0, + 'resources' => 0, + 'prompts' => 0, + 'resourceTemplates' => 0, + ]; + + try { + $finder = new Finder(); + $absolutePaths = []; + foreach ($directories as $dir) { + $path = rtrim($basePath, '/') . '/' . ltrim($dir, '/'); + if (is_dir($path)) { + $absolutePaths[] = $path; + } + } + + if (empty($absolutePaths)) { + $this->logger->warning('No valid discovery directories found to scan.', ['configured_paths' => $directories, 'base_path' => $basePath]); + + return; + } + + $finder->files() + ->in($absolutePaths) + ->exclude($excludeDirs) + ->name('*.php'); + + foreach ($finder as $file) { + $this->processFile($file, $discoveredCount); + } + } catch (Throwable $e) { + $this->logger->error('Error during file finding process for MCP discovery', [ + 'exception' => $e->getMessage(), + 'trace' => $e->getTraceAsString(), + ]); + } + + $duration = microtime(true) - $startTime; + $this->logger->info('Attribute discovery finished.', [ + 'duration_sec' => round($duration, 3), + 'tools' => $discoveredCount['tools'], + 'resources' => $discoveredCount['resources'], + 'prompts' => $discoveredCount['prompts'], + 'resourceTemplates' => $discoveredCount['resourceTemplates'], + ]); + } + + /** + * Process a single PHP file for MCP elements on classes or methods. + */ + private function processFile(SplFileInfo $file, array &$discoveredCount): void + { + $filePath = $file->getRealPath(); + if ($filePath === false) { + $this->logger->warning('Could not get real path for file', ['path' => $file->getPathname()]); + + return; + } + + $className = $this->getClassFromFile($filePath); + if (! $className) { + $this->logger->warning('No valid class found in file', ['file' => $filePath]); + + return; + } + + try { + $reflectionClass = new ReflectionClass($className); + + if ($reflectionClass->isAbstract() || $reflectionClass->isInterface() || $reflectionClass->isTrait() || $reflectionClass->isEnum()) { + return; + } + + $processedViaClassAttribute = false; + if ($reflectionClass->hasMethod('__invoke')) { + $invokeMethod = $reflectionClass->getMethod('__invoke'); + if ($invokeMethod->isPublic() && ! $invokeMethod->isStatic()) { + $attributeTypes = [McpTool::class, McpResource::class, McpPrompt::class, McpResourceTemplate::class]; + foreach ($attributeTypes as $attributeType) { + $classAttribute = $reflectionClass->getAttributes($attributeType, ReflectionAttribute::IS_INSTANCEOF)[0] ?? null; + if ($classAttribute) { + $this->processMethod($invokeMethod, $discoveredCount, $classAttribute); + $processedViaClassAttribute = true; + break; + } + } + } + } + + if (! $processedViaClassAttribute) { + foreach ($reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { + if ( + $method->getDeclaringClass()->getName() !== $reflectionClass->getName() || + $method->isStatic() || $method->isAbstract() || $method->isConstructor() || $method->isDestructor() || $method->getName() === '__invoke' + ) { + continue; + } + $attributeTypes = [McpTool::class, McpResource::class, McpPrompt::class, McpResourceTemplate::class]; + foreach ($attributeTypes as $attributeType) { + $methodAttribute = $method->getAttributes($attributeType, ReflectionAttribute::IS_INSTANCEOF)[0] ?? null; + if ($methodAttribute) { + $this->processMethod($method, $discoveredCount, $methodAttribute); + break; + } + } + } + } + } catch (ReflectionException $e) { + $this->logger->error('Reflection error processing file for MCP discovery', ['file' => $filePath, 'class' => $className, 'exception' => $e->getMessage()]); + } catch (Throwable $e) { + $this->logger->error('Unexpected error processing file for MCP discovery', [ + 'file' => $filePath, + 'class' => $className, + 'exception' => $e->getMessage(), + 'trace' => $e->getTraceAsString(), + ]); + } + } + + /** + * Process a method with a given MCP attribute instance. + * Can be called for regular methods or the __invoke method of an invokable class. + * + * @param ReflectionMethod $method The target method (e.g., regular method or __invoke). + * @param array $discoveredCount Pass by reference to update counts. + * @param ReflectionAttribute $attribute The ReflectionAttribute instance found (on method or class). + */ + private function processMethod(ReflectionMethod $method, array &$discoveredCount, ReflectionAttribute $attribute): void + { + $className = $method->getDeclaringClass()->getName(); + $classShortName = $method->getDeclaringClass()->getShortName(); + $methodName = $method->getName(); + $attributeClassName = $attribute->getName(); + + try { + $instance = $attribute->newInstance(); + + switch ($attributeClassName) { + case McpTool::class: + $docBlock = $this->docBlockParser->parseDocBlock($method->getDocComment() ?? null); + $name = $instance->name ?? ($methodName === '__invoke' ? $classShortName : $methodName); + $description = $instance->description ?? $this->docBlockParser->getSummary($docBlock) ?? null; + $inputSchema = $this->schemaGenerator->generate($method); + $tool = Tool::make($name, $inputSchema, $description, $instance->annotations); + $this->registry->registerTool($tool, [$className, $methodName]); + $discoveredCount['tools']++; + break; + + case McpResource::class: + $docBlock = $this->docBlockParser->parseDocBlock($method->getDocComment() ?? null); + $name = $instance->name ?? ($methodName === '__invoke' ? $classShortName : $methodName); + $description = $instance->description ?? $this->docBlockParser->getSummary($docBlock) ?? null; + $mimeType = $instance->mimeType; + $size = $instance->size; + $annotations = $instance->annotations; + $resource = Resource::make($instance->uri, $name, $description, $mimeType, $annotations, $size); + $this->registry->registerResource($resource, [$className, $methodName]); + $discoveredCount['resources']++; + break; + + case McpPrompt::class: + $docBlock = $this->docBlockParser->parseDocBlock($method->getDocComment() ?? null); + $name = $instance->name ?? ($methodName === '__invoke' ? $classShortName : $methodName); + $description = $instance->description ?? $this->docBlockParser->getSummary($docBlock) ?? null; + $arguments = []; + $paramTags = $this->docBlockParser->getParamTags($docBlock); + foreach ($method->getParameters() as $param) { + $reflectionType = $param->getType(); + if ($reflectionType instanceof \ReflectionNamedType && ! $reflectionType->isBuiltin()) { + continue; + } + $paramTag = $paramTags['$' . $param->getName()] ?? null; + $arguments[] = PromptArgument::make($param->getName(), $paramTag ? trim((string) $paramTag->getDescription()) : null, ! $param->isOptional() && ! $param->isDefaultValueAvailable()); + } + $prompt = Prompt::make($name, $description, $arguments); + $completionProviders = $this->getCompletionProviders($method); + $this->registry->registerPrompt($prompt, [$className, $methodName], $completionProviders); + $discoveredCount['prompts']++; + break; + + case McpResourceTemplate::class: + $docBlock = $this->docBlockParser->parseDocBlock($method->getDocComment() ?? null); + $name = $instance->name ?? ($methodName === '__invoke' ? $classShortName : $methodName); + $description = $instance->description ?? $this->docBlockParser->getSummary($docBlock) ?? null; + $mimeType = $instance->mimeType; + $annotations = $instance->annotations; + $resourceTemplate = ResourceTemplate::make($instance->uriTemplate, $name, $description, $mimeType, $annotations); + $completionProviders = $this->getCompletionProviders($method); + $this->registry->registerResourceTemplate($resourceTemplate, [$className, $methodName], $completionProviders); + $discoveredCount['resourceTemplates']++; + break; + } + } catch (McpServerException $e) { + $this->logger->error("Failed to process MCP attribute on {$className}::{$methodName}", ['attribute' => $attributeClassName, 'exception' => $e->getMessage(), 'trace' => $e->getPrevious() ? $e->getPrevious()->getTraceAsString() : $e->getTraceAsString()]); + } catch (Throwable $e) { + $this->logger->error("Unexpected error processing attribute on {$className}::{$methodName}", ['attribute' => $attributeClassName, 'exception' => $e->getMessage(), 'trace' => $e->getTraceAsString()]); + } + } + + private function getCompletionProviders(\ReflectionMethod $reflectionMethod): array + { + $completionProviders = []; + foreach ($reflectionMethod->getParameters() as $param) { + $reflectionType = $param->getType(); + if ($reflectionType instanceof \ReflectionNamedType && ! $reflectionType->isBuiltin()) { + continue; + } + + $completionAttributes = $param->getAttributes(CompletionProvider::class, \ReflectionAttribute::IS_INSTANCEOF); + if (!empty($completionAttributes)) { + $attributeInstance = $completionAttributes[0]->newInstance(); + + if ($attributeInstance->provider) { + $completionProviders[$param->getName()] = $attributeInstance->provider; + } elseif ($attributeInstance->providerClass) { + $completionProviders[$param->getName()] = $attributeInstance->provider; + } elseif ($attributeInstance->values) { + $completionProviders[$param->getName()] = new ListCompletionProvider($attributeInstance->values); + } elseif ($attributeInstance->enum) { + $completionProviders[$param->getName()] = new EnumCompletionProvider($attributeInstance->enum); + } + } + } + + return $completionProviders; + } + + /** + * Attempt to determine the FQCN from a PHP file path. + * Uses tokenization to extract namespace and class name. + * + * @param string $filePath Absolute path to the PHP file. + * @return class-string|null The FQCN or null if not found/determinable. + */ + private function getClassFromFile(string $filePath): ?string + { + if (! file_exists($filePath) || ! is_readable($filePath)) { + $this->logger->warning('File does not exist or is not readable.', ['file' => $filePath]); + + return null; + } + + try { + $content = file_get_contents($filePath); + if ($content === false) { + $this->logger->warning('Failed to read file content.', ['file' => $filePath]); + + return null; + } + if (strlen($content) > 500 * 1024) { + $this->logger->debug('Skipping large file during class discovery.', ['file' => $filePath]); + + return null; + } + + $tokens = token_get_all($content); + } catch (Throwable $e) { + $this->logger->warning("Failed to read or tokenize file during class discovery: {$filePath}", ['exception' => $e->getMessage()]); + + return null; + } + + $namespace = ''; + $namespaceFound = false; + $level = 0; + $potentialClasses = []; + + $tokenCount = count($tokens); + for ($i = 0; $i < $tokenCount; $i++) { + if (is_array($tokens[$i]) && $tokens[$i][0] === T_NAMESPACE) { + $namespace = ''; + for ($j = $i + 1; $j < $tokenCount; $j++) { + if ($tokens[$j] === ';' || $tokens[$j] === '{') { + $namespaceFound = true; + $i = $j; + break; + } + if (is_array($tokens[$j]) && in_array($tokens[$j][0], [T_STRING, T_NAME_QUALIFIED])) { + $namespace .= $tokens[$j][1]; + } elseif ($tokens[$j][0] === T_NS_SEPARATOR) { + $namespace .= '\\'; + } + } + if ($namespaceFound) { + break; + } + } + } + $namespace = trim($namespace, '\\'); + + for ($i = 0; $i < $tokenCount; $i++) { + $token = $tokens[$i]; + if ($token === '{') { + $level++; + + continue; + } + if ($token === '}') { + $level--; + + continue; + } + + if ($level === ($namespaceFound && str_contains($content, "namespace {$namespace} {") ? 1 : 0)) { + if (is_array($token) && in_array($token[0], [T_CLASS, T_INTERFACE, T_TRAIT, defined('T_ENUM') ? T_ENUM : -1])) { + for ($j = $i + 1; $j < $tokenCount; $j++) { + if (is_array($tokens[$j]) && $tokens[$j][0] === T_STRING) { + $className = $tokens[$j][1]; + $potentialClasses[] = $namespace ? $namespace . '\\' . $className : $className; + $i = $j; + break; + } + if ($tokens[$j] === ';' || $tokens[$j] === '{' || $tokens[$j] === ')') { + break; + } + } + } + } + } + + foreach ($potentialClasses as $potentialClass) { + if (class_exists($potentialClass, true)) { + return $potentialClass; + } + } + + if (! empty($potentialClasses)) { + if (! class_exists($potentialClasses[0], false)) { + $this->logger->debug('getClassFromFile returning potential non-class type. Are you sure this class has been autoloaded?', ['file' => $filePath, 'type' => $potentialClasses[0]]); + } + + return $potentialClasses[0]; + } + + return null; + } +} diff --git a/vendor/php-mcp/server/src/Utils/DocBlockParser.php b/vendor/php-mcp/server/src/Utils/DocBlockParser.php new file mode 100644 index 000000000..85bcb75e4 --- /dev/null +++ b/vendor/php-mcp/server/src/Utils/DocBlockParser.php @@ -0,0 +1,166 @@ +docBlockFactory = DocBlockFactory::createInstance(); + $this->logger = $logger ?? new NullLogger(); + } + + /** + * Safely parses a DocComment string into a DocBlock object. + */ + public function parseDocBlock(string|null|false $docComment): ?DocBlock + { + if ($docComment === false || $docComment === null || empty($docComment)) { + return null; + } + try { + return $this->docBlockFactory->create($docComment); + } catch (Throwable $e) { + // Log error or handle gracefully if invalid DocBlock syntax is encountered + $this->logger->warning('Failed to parse DocBlock', [ + 'error' => $e->getMessage(), + 'exception_trace' => $e->getTraceAsString(), + ]); + + return null; + } + } + + /** + * Gets the summary line from a DocBlock. + */ + public function getSummary(?DocBlock $docBlock): ?string + { + if (! $docBlock) { + return null; + } + $summary = trim($docBlock->getSummary()); + + return $summary ?: null; // Return null if empty after trimming + } + + /** + * Gets the description from a DocBlock (summary + description body). + */ + public function getDescription(?DocBlock $docBlock): ?string + { + if (! $docBlock) { + return null; + } + $summary = trim($docBlock->getSummary()); + $descriptionBody = trim((string) $docBlock->getDescription()); + + if ($summary && $descriptionBody) { + return $summary . "\n\n" . $descriptionBody; + } + if ($summary) { + return $summary; + } + if ($descriptionBody) { + return $descriptionBody; + } + + return null; + } + + /** + * Extracts @param tag information from a DocBlock, keyed by variable name (e.g., '$paramName'). + * + * @return array + */ + public function getParamTags(?DocBlock $docBlock): array + { + if (! $docBlock) { + return []; + } + + /** @var array $paramTags */ + $paramTags = []; + foreach ($docBlock->getTagsByName('param') as $tag) { + if ($tag instanceof Param && $tag->getVariableName()) { + $paramTags['$' . $tag->getVariableName()] = $tag; + } + } + + return $paramTags; + } + + /** + * Gets the @return tag information from a DocBlock. + */ + public function getReturnTag(?DocBlock $docBlock): ?Return_ + { + if (! $docBlock) { + return null; + } + /** @var Return_|null $returnTag */ + $returnTag = $docBlock->getTagsByName('return')[0] ?? null; + + return $returnTag; + } + + /** + * Gets the description string from a Param tag. + */ + public function getParamDescription(?Param $paramTag): ?string + { + return $paramTag ? (trim((string) $paramTag->getDescription()) ?: null) : null; + } + + /** + * Gets the type string from a Param tag. + */ + public function getParamTypeString(?Param $paramTag): ?string + { + if ($paramTag && $paramTag->getType()) { + $typeFromTag = trim((string) $paramTag->getType()); + if (! empty($typeFromTag)) { + return ltrim($typeFromTag, '\\'); + } + } + + return null; + } + + /** + * Gets the description string from a Return_ tag. + */ + public function getReturnDescription(?Return_ $returnTag): ?string + { + return $returnTag ? (trim((string) $returnTag->getDescription()) ?: null) : null; + } + + /** + * Gets the type string from a Return_ tag. + */ + public function getReturnTypeString(?Return_ $returnTag): ?string + { + if ($returnTag && $returnTag->getType()) { + $typeFromTag = trim((string) $returnTag->getType()); + if (! empty($typeFromTag)) { + return ltrim($typeFromTag, '\\'); + } + } + + return null; + } +} diff --git a/vendor/php-mcp/server/src/Utils/HandlerResolver.php b/vendor/php-mcp/server/src/Utils/HandlerResolver.php new file mode 100644 index 000000000..0f0e55f78 --- /dev/null +++ b/vendor/php-mcp/server/src/Utils/HandlerResolver.php @@ -0,0 +1,83 @@ +isPublic()) { + throw new InvalidArgumentException("Handler method '{$className}::{$methodName}' must be public."); + } + if ($reflectionMethod->isAbstract()) { + throw new InvalidArgumentException("Handler method '{$className}::{$methodName}' cannot be abstract."); + } + if ($reflectionMethod->isConstructor() || $reflectionMethod->isDestructor()) { + throw new InvalidArgumentException("Handler method '{$className}::{$methodName}' cannot be a constructor or destructor."); + } + + return $reflectionMethod; + } catch (ReflectionException $e) { + // This typically occurs if class_exists passed but ReflectionMethod still fails (rare) + throw new InvalidArgumentException("Reflection error for handler '{$className}::{$methodName}': {$e->getMessage()}", 0, $e); + } + } +} diff --git a/vendor/php-mcp/server/src/Utils/SchemaGenerator.php b/vendor/php-mcp/server/src/Utils/SchemaGenerator.php new file mode 100644 index 000000000..16e4a2c79 --- /dev/null +++ b/vendor/php-mcp/server/src/Utils/SchemaGenerator.php @@ -0,0 +1,778 @@ +docBlockParser = $docBlockParser; + } + + /** + * Generates a JSON Schema object (as a PHP array) for a method's or function's parameters. + */ + public function generate(\ReflectionMethod|\ReflectionFunction $reflection): array + { + $methodSchema = $this->extractMethodLevelSchema($reflection); + + if ($methodSchema && isset($methodSchema['definition'])) { + return $methodSchema['definition']; + } + + $parametersInfo = $this->parseParametersInfo($reflection); + + return $this->buildSchemaFromParameters($parametersInfo, $methodSchema); + } + + /** + * Extracts method-level or function-level Schema attribute. + */ + private function extractMethodLevelSchema(\ReflectionMethod|\ReflectionFunction $reflection): ?array + { + $schemaAttrs = $reflection->getAttributes(Schema::class, \ReflectionAttribute::IS_INSTANCEOF); + if (empty($schemaAttrs)) { + return null; + } + + $schemaAttr = $schemaAttrs[0]->newInstance(); + return $schemaAttr->toArray(); + } + + /** + * Extracts parameter-level Schema attribute. + */ + private function extractParameterLevelSchema(ReflectionParameter $parameter): array + { + $schemaAttrs = $parameter->getAttributes(Schema::class, \ReflectionAttribute::IS_INSTANCEOF); + if (empty($schemaAttrs)) { + return []; + } + + $schemaAttr = $schemaAttrs[0]->newInstance(); + return $schemaAttr->toArray(); + } + + /** + * Builds the final schema from parameter information and method-level schema. + * + * @param array + * }> $parametersInfo + * + * @param array|null $methodSchema + * + * @return array + */ + private function buildSchemaFromParameters(array $parametersInfo, ?array $methodSchema): array + { + $schema = [ + 'type' => 'object', + 'properties' => [], + 'required' => [], + ]; + + // Apply method-level schema as base + if ($methodSchema) { + $schema = array_merge($schema, $methodSchema); + if (!isset($schema['type'])) { + $schema['type'] = 'object'; + } + if (!isset($schema['properties'])) { + $schema['properties'] = []; + } + if (!isset($schema['required'])) { + $schema['required'] = []; + } + } + + foreach ($parametersInfo as $paramInfo) { + $paramName = $paramInfo['name']; + + $methodLevelParamSchema = $schema['properties'][$paramName] ?? null; + + $paramSchema = $this->buildParameterSchema($paramInfo, $methodLevelParamSchema); + + $schema['properties'][$paramName] = $paramSchema; + + if ($paramInfo['required'] && !in_array($paramName, $schema['required'])) { + $schema['required'][] = $paramName; + } elseif (!$paramInfo['required'] && ($key = array_search($paramName, $schema['required'])) !== false) { + unset($schema['required'][$key]); + $schema['required'] = array_values($schema['required']); // Re-index + } + } + + // Clean up empty properties + if (empty($schema['properties'])) { + $schema['properties'] = new stdClass(); + } + if (empty($schema['required'])) { + unset($schema['required']); + } + + return $schema; + } + + /** + * Builds the final schema for a single parameter by merging all three levels. + * + * @param array{ + * name: string, + * doc_block_tag: Param|null, + * reflection_param: ReflectionParameter, + * reflection_type_object: ReflectionType|null, + * type_string: string, + * description: string|null, + * required: bool, + * allows_null: bool, + * default_value: mixed|null, + * has_default: bool, + * is_variadic: bool, + * parameter_schema: array + * } $paramInfo + * @param array|null $methodLevelParamSchema + */ + private function buildParameterSchema(array $paramInfo, ?array $methodLevelParamSchema = null): array + { + if ($paramInfo['is_variadic']) { + return $this->buildVariadicParameterSchema($paramInfo); + } + + $inferredSchema = $this->buildInferredParameterSchema($paramInfo); + + // Method-level takes precedence over inferred schema + $mergedSchema = $inferredSchema; + if ($methodLevelParamSchema) { + $mergedSchema = $this->mergeSchemas($inferredSchema, $methodLevelParamSchema); + } + + // Parameter-level takes highest precedence + $parameterLevelSchema = $paramInfo['parameter_schema']; + if (!empty($parameterLevelSchema)) { + $mergedSchema = $this->mergeSchemas($mergedSchema, $parameterLevelSchema); + } + + return $mergedSchema; + } + + /** + * Merge two schemas where the dominant schema takes precedence over the recessive one. + * + * @param array $recessiveSchema The schema with lower precedence + * @param array $dominantSchema The schema with higher precedence + */ + private function mergeSchemas(array $recessiveSchema, array $dominantSchema): array + { + $mergedSchema = array_merge($recessiveSchema, $dominantSchema); + + return $mergedSchema; + } + + /** + * Builds parameter schema from inferred type and docblock information only. + * Returns empty array for variadic parameters (handled separately). + */ + private function buildInferredParameterSchema(array $paramInfo): array + { + $paramSchema = []; + + // Variadic parameters are handled separately + if ($paramInfo['is_variadic']) { + return []; + } + + // Infer JSON Schema types + $jsonTypes = $this->inferParameterTypes($paramInfo); + + if (count($jsonTypes) === 1) { + $paramSchema['type'] = $jsonTypes[0]; + } elseif (count($jsonTypes) > 1) { + $paramSchema['type'] = $jsonTypes; + } + + // Add description from docblock + if ($paramInfo['description']) { + $paramSchema['description'] = $paramInfo['description']; + } + + // Add default value only if parameter actually has a default + if ($paramInfo['has_default']) { + $paramSchema['default'] = $paramInfo['default_value']; + } + + // Handle enums + $paramSchema = $this->applyEnumConstraints($paramSchema, $paramInfo); + + // Handle array items + $paramSchema = $this->applyArrayConstraints($paramSchema, $paramInfo); + + return $paramSchema; + } + + /** + * Builds schema for variadic parameters. + */ + private function buildVariadicParameterSchema(array $paramInfo): array + { + $paramSchema = ['type' => 'array']; + + // Apply parameter-level Schema attributes first + if (!empty($paramInfo['parameter_schema'])) { + $paramSchema = array_merge($paramSchema, $paramInfo['parameter_schema']); + // Ensure type is always array for variadic + $paramSchema['type'] = 'array'; + } + + if ($paramInfo['description']) { + $paramSchema['description'] = $paramInfo['description']; + } + + // If no items specified by Schema attribute, infer from type + if (!isset($paramSchema['items'])) { + $itemJsonTypes = $this->mapPhpTypeToJsonSchemaType($paramInfo['type_string']); + $nonNullItemTypes = array_filter($itemJsonTypes, fn($t) => $t !== 'null'); + + if (count($nonNullItemTypes) === 1) { + $paramSchema['items'] = ['type' => $nonNullItemTypes[0]]; + } + } + + return $paramSchema; + } + + /** + * Infers JSON Schema types for a parameter. + */ + private function inferParameterTypes(array $paramInfo): array + { + $jsonTypes = $this->mapPhpTypeToJsonSchemaType($paramInfo['type_string']); + + if ($paramInfo['allows_null'] && strtolower($paramInfo['type_string']) !== 'mixed' && !in_array('null', $jsonTypes)) { + $jsonTypes[] = 'null'; + } + + if (count($jsonTypes) > 1) { + // Sort but ensure null comes first for consistency + $nullIndex = array_search('null', $jsonTypes); + if ($nullIndex !== false) { + unset($jsonTypes[$nullIndex]); + sort($jsonTypes); + array_unshift($jsonTypes, 'null'); + } else { + sort($jsonTypes); + } + } + + return $jsonTypes; + } + + /** + * Applies enum constraints to parameter schema. + */ + private function applyEnumConstraints(array $paramSchema, array $paramInfo): array + { + $reflectionType = $paramInfo['reflection_type_object']; + + if (!($reflectionType instanceof ReflectionNamedType) || $reflectionType->isBuiltin() || !enum_exists($reflectionType->getName())) { + return $paramSchema; + } + + $enumClass = $reflectionType->getName(); + $enumReflection = new ReflectionEnum($enumClass); + $backingTypeReflection = $enumReflection->getBackingType(); + + if ($enumReflection->isBacked() && $backingTypeReflection instanceof ReflectionNamedType) { + $paramSchema['enum'] = array_column($enumClass::cases(), 'value'); + $jsonBackingType = match ($backingTypeReflection->getName()) { + 'int' => 'integer', + 'string' => 'string', + default => null, + }; + + if ($jsonBackingType) { + if (isset($paramSchema['type']) && is_array($paramSchema['type']) && in_array('null', $paramSchema['type'])) { + $paramSchema['type'] = ['null', $jsonBackingType]; + } else { + $paramSchema['type'] = $jsonBackingType; + } + } + } else { + // Non-backed enum - use names as enum values + $paramSchema['enum'] = array_column($enumClass::cases(), 'name'); + if (isset($paramSchema['type']) && is_array($paramSchema['type']) && in_array('null', $paramSchema['type'])) { + $paramSchema['type'] = ['null', 'string']; + } else { + $paramSchema['type'] = 'string'; + } + } + + return $paramSchema; + } + + /** + * Applies array-specific constraints to parameter schema. + */ + private function applyArrayConstraints(array $paramSchema, array $paramInfo): array + { + if (!isset($paramSchema['type'])) { + return $paramSchema; + } + + $typeString = $paramInfo['type_string']; + $allowsNull = $paramInfo['allows_null']; + + // Handle object-like arrays using array{} syntax + if (preg_match('/^array\s*{/i', $typeString)) { + $objectSchema = $this->inferArrayItemsType($typeString); + if (is_array($objectSchema) && isset($objectSchema['properties'])) { + $paramSchema = array_merge($paramSchema, $objectSchema); + $paramSchema['type'] = $allowsNull ? ['object', 'null'] : 'object'; + } + } + // Handle regular arrays + elseif (in_array('array', $this->mapPhpTypeToJsonSchemaType($typeString))) { + $itemsType = $this->inferArrayItemsType($typeString); + if ($itemsType !== 'any') { + if (is_string($itemsType)) { + $paramSchema['items'] = ['type' => $itemsType]; + } else { + if (!isset($itemsType['type']) && isset($itemsType['properties'])) { + $itemsType = array_merge(['type' => 'object'], $itemsType); + } + $paramSchema['items'] = $itemsType; + } + } + + if ($allowsNull) { + $paramSchema['type'] = ['array', 'null']; + sort($paramSchema['type']); + } else { + $paramSchema['type'] = 'array'; + } + } + + return $paramSchema; + } + + /** + * Parses detailed information about a method's parameters. + * + * @return array + * }> + */ + private function parseParametersInfo(\ReflectionMethod|\ReflectionFunction $reflection): array + { + $docComment = $reflection->getDocComment() ?: null; + $docBlock = $this->docBlockParser->parseDocBlock($docComment); + $paramTags = $this->docBlockParser->getParamTags($docBlock); + $parametersInfo = []; + + foreach ($reflection->getParameters() as $rp) { + $paramName = $rp->getName(); + $paramTag = $paramTags['$' . $paramName] ?? null; + + $reflectionType = $rp->getType(); + $typeString = $this->getParameterTypeString($rp, $paramTag); + $description = $this->docBlockParser->getParamDescription($paramTag); + $hasDefault = $rp->isDefaultValueAvailable(); + $defaultValue = $hasDefault ? $rp->getDefaultValue() : null; + $isVariadic = $rp->isVariadic(); + + $parameterSchema = $this->extractParameterLevelSchema($rp); + + if ($defaultValue instanceof \BackedEnum) { + $defaultValue = $defaultValue->value; + } + + if ($defaultValue instanceof \UnitEnum) { + $defaultValue = $defaultValue->name; + } + + $allowsNull = false; + if ($reflectionType && $reflectionType->allowsNull()) { + $allowsNull = true; + } elseif ($hasDefault && $defaultValue === null) { + $allowsNull = true; + } elseif (str_contains($typeString, 'null') || strtolower($typeString) === 'mixed') { + $allowsNull = true; + } + + $parametersInfo[] = [ + 'name' => $paramName, + 'doc_block_tag' => $paramTag, + 'reflection_param' => $rp, + 'reflection_type_object' => $reflectionType, + 'type_string' => $typeString, + 'description' => $description, + 'required' => !$rp->isOptional(), + 'allows_null' => $allowsNull, + 'default_value' => $defaultValue, + 'has_default' => $hasDefault, + 'is_variadic' => $isVariadic, + 'parameter_schema' => $parameterSchema, + ]; + } + + return $parametersInfo; + } + + /** + * Determines the type string for a parameter, prioritizing DocBlock. + */ + private function getParameterTypeString(ReflectionParameter $rp, ?Param $paramTag): string + { + $docBlockType = $this->docBlockParser->getParamTypeString($paramTag); + $isDocBlockTypeGeneric = false; + + if ($docBlockType !== null) { + if (in_array(strtolower($docBlockType), ['mixed', 'unknown', ''])) { + $isDocBlockTypeGeneric = true; + } + } else { + $isDocBlockTypeGeneric = true; // No tag or no type in tag implies generic + } + + $reflectionType = $rp->getType(); + $reflectionTypeString = null; + if ($reflectionType) { + $reflectionTypeString = $this->getTypeStringFromReflection($reflectionType, $rp->allowsNull()); + } + + // Prioritize Reflection if DocBlock type is generic AND Reflection provides a more specific type + if ($isDocBlockTypeGeneric && $reflectionTypeString !== null && $reflectionTypeString !== 'mixed') { + return $reflectionTypeString; + } + + // Otherwise, use the DocBlock type if it was valid and non-generic + if ($docBlockType !== null && !$isDocBlockTypeGeneric) { + // Consider if DocBlock adds nullability missing from reflection + if (stripos($docBlockType, 'null') !== false && $reflectionTypeString && stripos($reflectionTypeString, 'null') === false && !str_ends_with($reflectionTypeString, '|null')) { + // If reflection didn't capture null, but docblock did, append |null (if not already mixed) + if ($reflectionTypeString !== 'mixed') { + return $reflectionTypeString . '|null'; + } + } + + return $docBlockType; + } + + // Fallback to Reflection type even if it was generic ('mixed') + if ($reflectionTypeString !== null) { + return $reflectionTypeString; + } + + // Default to 'mixed' if nothing else found + return 'mixed'; + } + + /** + * Converts a ReflectionType object into a type string representation. + */ + private function getTypeStringFromReflection(?ReflectionType $type, bool $nativeAllowsNull): string + { + if ($type === null) { + return 'mixed'; + } + + $types = []; + if ($type instanceof ReflectionUnionType) { + foreach ($type->getTypes() as $innerType) { + $types[] = $this->getTypeStringFromReflection($innerType, $innerType->allowsNull()); + } + if ($nativeAllowsNull) { + $types = array_filter($types, fn($t) => strtolower($t) !== 'null'); + } + $typeString = implode('|', array_unique(array_filter($types))); + } elseif ($type instanceof ReflectionIntersectionType) { + foreach ($type->getTypes() as $innerType) { + $types[] = $this->getTypeStringFromReflection($innerType, false); + } + $typeString = implode('&', array_unique(array_filter($types))); + } elseif ($type instanceof ReflectionNamedType) { + $typeString = $type->getName(); + } else { + return 'mixed'; + } + + $typeString = match (strtolower($typeString)) { + 'bool' => 'boolean', + 'int' => 'integer', + 'float', 'double' => 'number', + 'str' => 'string', + default => $typeString, + }; + + $isNullable = $nativeAllowsNull; + if ($type instanceof ReflectionNamedType && $type->getName() === 'mixed') { + $isNullable = true; + } + + if ($type instanceof ReflectionUnionType && !$nativeAllowsNull) { + foreach ($type->getTypes() as $innerType) { + if ($innerType instanceof ReflectionNamedType && strtolower($innerType->getName()) === 'null') { + $isNullable = true; + break; + } + } + } + + if ($isNullable && $typeString !== 'mixed' && stripos($typeString, 'null') === false) { + if (!str_ends_with($typeString, '|null') && !str_ends_with($typeString, '&null')) { + $typeString .= '|null'; + } + } + + // Remove leading backslash from class names, but handle built-ins like 'int' or unions like 'int|string' + if (str_contains($typeString, '\\')) { + $parts = preg_split('/([|&])/', $typeString, -1, PREG_SPLIT_DELIM_CAPTURE); + $processedParts = array_map(fn($part) => str_starts_with($part, '\\') ? ltrim($part, '\\') : $part, $parts); + $typeString = implode('', $processedParts); + } + + return $typeString ?: 'mixed'; + } + + /** + * Maps a PHP type string (potentially a union) to an array of JSON Schema type names. + */ + private function mapPhpTypeToJsonSchemaType(string $phpTypeString): array + { + $normalizedType = strtolower(trim($phpTypeString)); + + // PRIORITY 1: Check for array{} syntax which should be treated as object + if (preg_match('/^array\s*{/i', $normalizedType)) { + return ['object']; + } + + // PRIORITY 2: Check for array syntax first (T[] or generics) + if ( + str_contains($normalizedType, '[]') || + preg_match('/^(array|list|iterable|collection)mapPhpTypeToJsonSchemaType(trim($type)); + $jsonTypes = array_merge($jsonTypes, $mapped); + } + + return array_values(array_unique($jsonTypes)); + } + + // PRIORITY 4: Handle simple built-in types + return match ($normalizedType) { + 'string', 'scalar' => ['string'], + '?string' => ['null', 'string'], + 'int', 'integer' => ['integer'], + '?int', '?integer' => ['null', 'integer'], + 'float', 'double', 'number' => ['number'], + '?float', '?double', '?number' => ['null', 'number'], + 'bool', 'boolean' => ['boolean'], + '?bool', '?boolean' => ['null', 'boolean'], + 'array' => ['array'], + '?array' => ['null', 'array'], + 'object', 'stdclass' => ['object'], + '?object', '?stdclass' => ['null', 'object'], + 'null' => ['null'], + 'resource', 'callable' => ['object'], + 'mixed' => [], + 'void', 'never' => [], + default => ['object'], + }; + } + + /** + * Infers the 'items' schema type for an array based on DocBlock type hints. + */ + private function inferArrayItemsType(string $phpTypeString): string|array + { + $normalizedType = trim($phpTypeString); + + // Case 1: Simple T[] syntax (e.g., string[], int[], bool[], etc.) + if (preg_match('/^(\\??)([\w\\\\]+)\\s*\\[\\]$/i', $normalizedType, $matches)) { + $itemType = strtolower($matches[2]); + return $this->mapSimpleTypeToJsonSchema($itemType); + } + + // Case 2: Generic array syntax (e.g., array, array, etc.) + if (preg_match('/^(\\??)array\s*<\s*([\w\\\\|]+)\s*>$/i', $normalizedType, $matches)) { + $itemType = strtolower($matches[2]); + return $this->mapSimpleTypeToJsonSchema($itemType); + } + + // Case 3: Nested array> syntax or T[][] syntax + if ( + preg_match('/^(\\??)array\s*<\s*array\s*<\s*([\w\\\\|]+)\s*>\s*>$/i', $normalizedType, $matches) || + preg_match('/^(\\??)([\w\\\\]+)\s*\[\]\[\]$/i', $normalizedType, $matches) + ) { + $innerType = $this->mapSimpleTypeToJsonSchema(isset($matches[2]) ? strtolower($matches[2]) : 'any'); + // Return a schema for array with items being arrays + return [ + 'type' => 'array', + 'items' => [ + 'type' => $innerType + ] + ]; + } + + // Case 4: Object-like array syntax (e.g., array{name: string, age: int}) + if (preg_match('/^(\\??)array\s*\{(.+)\}$/is', $normalizedType, $matches)) { + return $this->parseObjectLikeArray($matches[2]); + } + + return 'any'; + } + + /** + * Parses object-like array syntax into a JSON Schema object + */ + private function parseObjectLikeArray(string $propertiesStr): array + { + $properties = []; + $required = []; + + // Parse properties from the string, handling nested structures + $depth = 0; + $buffer = ''; + + for ($i = 0; $i < strlen($propertiesStr); $i++) { + $char = $propertiesStr[$i]; + + // Track nested braces + if ($char === '{') { + $depth++; + $buffer .= $char; + } elseif ($char === '}') { + $depth--; + $buffer .= $char; + } + // Property separator (comma) + elseif ($char === ',' && $depth === 0) { + // Process the completed property + $this->parsePropertyDefinition(trim($buffer), $properties, $required); + $buffer = ''; + } else { + $buffer .= $char; + } + } + + // Process the last property + if (!empty($buffer)) { + $this->parsePropertyDefinition(trim($buffer), $properties, $required); + } + + if (!empty($properties)) { + return [ + 'type' => 'object', + 'properties' => $properties, + 'required' => $required + ]; + } + + return ['type' => 'object']; + } + + /** + * Parses a single property definition from an object-like array syntax + */ + private function parsePropertyDefinition(string $propDefinition, array &$properties, array &$required): void + { + // Match property name and type + if (preg_match('/^([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)\s*:\s*(.+)$/i', $propDefinition, $matches)) { + $propName = $matches[1]; + $propType = trim($matches[2]); + + // Add to required properties + $required[] = $propName; + + // Check for nested array{} syntax + if (preg_match('/^array\s*\{(.+)\}$/is', $propType, $nestedMatches)) { + $nestedSchema = $this->parseObjectLikeArray($nestedMatches[1]); + $properties[$propName] = $nestedSchema; + } + // Check for array or T[] syntax + elseif ( + preg_match('/^array\s*<\s*([\w\\\\|]+)\s*>$/i', $propType, $arrayMatches) || + preg_match('/^([\w\\\\]+)\s*\[\]$/i', $propType, $arrayMatches) + ) { + $itemType = $arrayMatches[1] ?? 'any'; + $properties[$propName] = [ + 'type' => 'array', + 'items' => [ + 'type' => $this->mapSimpleTypeToJsonSchema($itemType) + ] + ]; + } + // Simple type + else { + $properties[$propName] = ['type' => $this->mapSimpleTypeToJsonSchema($propType)]; + } + } + } + + /** + * Helper method to map basic PHP types to JSON Schema types + */ + private function mapSimpleTypeToJsonSchema(string $type): string + { + return match (strtolower($type)) { + 'string' => 'string', + 'int', 'integer' => 'integer', + 'bool', 'boolean' => 'boolean', + 'float', 'double', 'number' => 'number', + 'array' => 'array', + 'object', 'stdclass' => 'object', + default => in_array(strtolower($type), ['datetime', 'datetimeinterface']) ? 'string' : 'object', + }; + } +} diff --git a/vendor/php-mcp/server/src/Utils/SchemaValidator.php b/vendor/php-mcp/server/src/Utils/SchemaValidator.php new file mode 100644 index 000000000..a1e499516 --- /dev/null +++ b/vendor/php-mcp/server/src/Utils/SchemaValidator.php @@ -0,0 +1,317 @@ +logger = $logger; + } + + /** + * Validates data against a JSON schema. + * + * @param mixed $data The data to validate (should generally be decoded JSON). + * @param array|object $schema The JSON Schema definition (as PHP array or object). + * @return list Array of validation errors, empty if valid. + */ + public function validateAgainstJsonSchema(mixed $data, array|object $schema): array + { + if (is_array($data) && empty($data)) { + $data = new \stdClass(); + } + + try { + // --- Schema Preparation --- + if (is_array($schema)) { + $schemaJson = json_encode($schema, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES); + $schemaObject = json_decode($schemaJson, false, 512, JSON_THROW_ON_ERROR); + } elseif (is_object($schema)) { + // This might be overly cautious but safer against varied inputs. + $schemaJson = json_encode($schema, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES); + $schemaObject = json_decode($schemaJson, false, 512, JSON_THROW_ON_ERROR); + } else { + throw new InvalidArgumentException('Schema must be an array or object.'); + } + + // --- Data Preparation --- + // Opis Validator generally prefers objects for object validation + $dataToValidate = $this->convertDataForValidator($data); + } catch (JsonException $e) { + $this->logger->error('MCP SDK: Invalid schema structure provided for validation (JSON conversion failed).', ['exception' => $e]); + + return [['pointer' => '', 'keyword' => 'internal', 'message' => 'Invalid schema definition provided (JSON error).']]; + } catch (InvalidArgumentException $e) { + $this->logger->error('MCP SDK: Invalid schema structure provided for validation.', ['exception' => $e]); + + return [['pointer' => '', 'keyword' => 'internal', 'message' => $e->getMessage()]]; + } catch (Throwable $e) { + $this->logger->error('MCP SDK: Error preparing data/schema for validation.', ['exception' => $e]); + + return [['pointer' => '', 'keyword' => 'internal', 'message' => 'Internal validation preparation error.']]; + } + + $validator = $this->getJsonSchemaValidator(); + + try { + $result = $validator->validate($dataToValidate, $schemaObject); + } catch (Throwable $e) { + $this->logger->error('MCP SDK: JSON Schema validation failed internally.', [ + 'exception_message' => $e->getMessage(), + 'exception_trace' => $e->getTraceAsString(), + 'data' => json_encode($dataToValidate), + 'schema' => json_encode($schemaObject), + ]); + + return [['pointer' => '', 'keyword' => 'internal', 'message' => 'Schema validation process failed: ' . $e->getMessage()]]; + } + + if ($result->isValid()) { + return []; + } + + $formattedErrors = []; + $topError = $result->error(); + + if ($topError) { + $this->collectSubErrors($topError, $formattedErrors); + } + + if (empty($formattedErrors) && $topError) { // Fallback + $formattedErrors[] = [ + 'pointer' => $this->formatJsonPointerPath($topError->data()?->path()), + 'keyword' => $topError->keyword(), + 'message' => $this->formatValidationError($topError), + ]; + } + + return $formattedErrors; + } + + /** + * Get or create the JSON Schema validator instance. + */ + private function getJsonSchemaValidator(): Validator + { + if ($this->jsonSchemaValidator === null) { + $this->jsonSchemaValidator = new Validator(); + // Potentially configure resolver here if needed later + } + + return $this->jsonSchemaValidator; + } + + /** + * Recursively converts associative arrays to stdClass objects for validator compatibility. + */ + private function convertDataForValidator(mixed $data): mixed + { + if (is_array($data)) { + // Check if it's an associative array (keys are not sequential numbers 0..N-1) + if (! empty($data) && array_keys($data) !== range(0, count($data) - 1)) { + $obj = new \stdClass(); + foreach ($data as $key => $value) { + $obj->{$key} = $this->convertDataForValidator($value); + } + + return $obj; + } else { + // It's a list (sequential array), convert items recursively + return array_map([$this, 'convertDataForValidator'], $data); + } + } elseif (is_object($data) && $data instanceof \stdClass) { + // Deep copy/convert stdClass objects as well + $obj = new \stdClass(); + foreach (get_object_vars($data) as $key => $value) { + $obj->{$key} = $this->convertDataForValidator($value); + } + + return $obj; + } + + // Leave other objects and scalar types as they are + return $data; + } + + /** + * Recursively collects leaf validation errors. + */ + private function collectSubErrors(ValidationError $error, array &$collectedErrors): void + { + $subErrors = $error->subErrors(); + if (empty($subErrors)) { + $collectedErrors[] = [ + 'pointer' => $this->formatJsonPointerPath($error->data()?->path()), + 'keyword' => $error->keyword(), + 'message' => $this->formatValidationError($error), + ]; + } else { + foreach ($subErrors as $subError) { + $this->collectSubErrors($subError, $collectedErrors); + } + } + } + + /** + * Formats the path array into a JSON Pointer string. + */ + private function formatJsonPointerPath(?array $pathComponents): string + { + if ($pathComponents === null || empty($pathComponents)) { + return '/'; + } + $escapedComponents = array_map(function ($component) { + $componentStr = (string) $component; + + return str_replace(['~', '/'], ['~0', '~1'], $componentStr); + }, $pathComponents); + + return '/' . implode('/', $escapedComponents); + } + + /** + * Formats an Opis SchemaValidationError into a user-friendly message. + */ + private function formatValidationError(ValidationError $error): string + { + $keyword = $error->keyword(); + $args = $error->args(); + $message = "Constraint `{$keyword}` failed."; + + switch (strtolower($keyword)) { + case 'required': + $missing = $args['missing'] ?? []; + $formattedMissing = implode(', ', array_map(fn($p) => "`{$p}`", $missing)); + $message = "Missing required properties: {$formattedMissing}."; + break; + case 'type': + $expected = implode('|', (array) ($args['expected'] ?? [])); + $used = $args['used'] ?? 'unknown'; + $message = "Invalid type. Expected `{$expected}`, but received `{$used}`."; + break; + case 'enum': + $schemaData = $error->schema()?->info()?->data(); + $allowedValues = []; + if (is_object($schemaData) && property_exists($schemaData, 'enum') && is_array($schemaData->enum)) { + $allowedValues = $schemaData->enum; + } elseif (is_array($schemaData) && isset($schemaData['enum']) && is_array($schemaData['enum'])) { + $allowedValues = $schemaData['enum']; + } else { + $this->logger->warning("MCP SDK: Could not retrieve 'enum' values from schema info for error.", ['error_args' => $args]); + } + if (empty($allowedValues)) { + $message = 'Value does not match the allowed enumeration.'; + } else { + $formattedAllowed = array_map(function ($v) { /* ... formatting logic ... */ + if (is_string($v)) { + return '"' . $v . '"'; + } + if (is_bool($v)) { + return $v ? 'true' : 'false'; + } + if ($v === null) { + return 'null'; + } + + return (string) $v; + }, $allowedValues); + $message = 'Value must be one of the allowed values: ' . implode(', ', $formattedAllowed) . '.'; + } + break; + case 'const': + $expected = json_encode($args['expected'] ?? 'null', JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + $message = "Value must be equal to the constant value: {$expected}."; + break; + case 'minLength': // Corrected casing + $min = $args['min'] ?? '?'; + $message = "String must be at least {$min} characters long."; + break; + case 'maxLength': // Corrected casing + $max = $args['max'] ?? '?'; + $message = "String must not be longer than {$max} characters."; + break; + case 'pattern': + $pattern = $args['pattern'] ?? '?'; + $message = "String does not match the required pattern: `{$pattern}`."; + break; + case 'minimum': + $min = $args['min'] ?? '?'; + $message = "Number must be greater than or equal to {$min}."; + break; + case 'maximum': + $max = $args['max'] ?? '?'; + $message = "Number must be less than or equal to {$max}."; + break; + case 'exclusiveMinimum': // Corrected casing + $min = $args['min'] ?? '?'; + $message = "Number must be strictly greater than {$min}."; + break; + case 'exclusiveMaximum': // Corrected casing + $max = $args['max'] ?? '?'; + $message = "Number must be strictly less than {$max}."; + break; + case 'multipleOf': // Corrected casing + $value = $args['value'] ?? '?'; + $message = "Number must be a multiple of {$value}."; + break; + case 'minItems': // Corrected casing + $min = $args['min'] ?? '?'; + $message = "Array must contain at least {$min} items."; + break; + case 'maxItems': // Corrected casing + $max = $args['max'] ?? '?'; + $message = "Array must contain no more than {$max} items."; + break; + case 'uniqueItems': // Corrected casing + $message = 'Array items must be unique.'; + break; + case 'minProperties': // Corrected casing + $min = $args['min'] ?? '?'; + $message = "Object must have at least {$min} properties."; + break; + case 'maxProperties': // Corrected casing + $max = $args['max'] ?? '?'; + $message = "Object must have no more than {$max} properties."; + break; + case 'additionalProperties': // Corrected casing + $unexpected = $args['properties'] ?? []; + $formattedUnexpected = implode(', ', array_map(fn($p) => "`{$p}`", $unexpected)); + $message = "Object contains unexpected additional properties: {$formattedUnexpected}."; + break; + case 'format': + $format = $args['format'] ?? 'unknown'; + $message = "Value does not match the required format: `{$format}`."; + break; + default: + $builtInMessage = $error->message(); + if ($builtInMessage && $builtInMessage !== 'The data must match the schema') { + $placeholders = $args ?? []; + $builtInMessage = preg_replace_callback('/\{(\w+)\}/', function ($match) use ($placeholders) { + $key = $match[1]; + $value = $placeholders[$key] ?? '{' . $key . '}'; + + return is_array($value) ? json_encode($value) : (string) $value; + }, $builtInMessage); + $message = $builtInMessage; + } + break; + } + + return $message; + } +} diff --git a/vendor/php-mcp/server/tests/Fixtures/Discovery/DiscoverablePromptHandler.php b/vendor/php-mcp/server/tests/Fixtures/Discovery/DiscoverablePromptHandler.php new file mode 100644 index 000000000..985cbe8c0 --- /dev/null +++ b/vendor/php-mcp/server/tests/Fixtures/Discovery/DiscoverablePromptHandler.php @@ -0,0 +1,38 @@ + "user", "content" => "Write a {$genre} story about a lost robot, approximately {$lengthWords} words long."] + ]; + } + + #[McpPrompt] + public function simpleQuestionPrompt(string $question): array + { + return [ + ["role" => "user", "content" => $question], + ["role" => "assistant", "content" => "I will try to answer that."] + ]; + } +} diff --git a/vendor/php-mcp/server/tests/Fixtures/Discovery/DiscoverableResourceHandler.php b/vendor/php-mcp/server/tests/Fixtures/Discovery/DiscoverableResourceHandler.php new file mode 100644 index 000000000..8c8affa17 --- /dev/null +++ b/vendor/php-mcp/server/tests/Fixtures/Discovery/DiscoverableResourceHandler.php @@ -0,0 +1,42 @@ + "dark", "fontSize" => 14]; + } + + public function someOtherMethod(): void + { + } +} diff --git a/vendor/php-mcp/server/tests/Fixtures/Discovery/DiscoverableTemplateHandler.php b/vendor/php-mcp/server/tests/Fixtures/Discovery/DiscoverableTemplateHandler.php new file mode 100644 index 000000000..29917cb18 --- /dev/null +++ b/vendor/php-mcp/server/tests/Fixtures/Discovery/DiscoverableTemplateHandler.php @@ -0,0 +1,42 @@ + $productId, + "name" => "Product " . $productId, + "region" => $region, + "price" => ($region === "EU" ? "€" : "$") . (hexdec(substr(md5($productId), 0, 4)) / 100) + ]; + } + + #[McpResourceTemplate(uriTemplate: "file://{path}/{filename}.{extension}")] + public function getFileContent(string $path, string $filename, string $extension): string + { + return "Content of {$path}/{$filename}.{$extension}"; + } +} diff --git a/vendor/php-mcp/server/tests/Fixtures/Discovery/DiscoverableToolHandler.php b/vendor/php-mcp/server/tests/Fixtures/Discovery/DiscoverableToolHandler.php new file mode 100644 index 000000000..c449c05d3 --- /dev/null +++ b/vendor/php-mcp/server/tests/Fixtures/Discovery/DiscoverableToolHandler.php @@ -0,0 +1,57 @@ + $count, 'loudly' => $loudly, 'mode' => $mode->value, 'message' => "Action repeated."]; + } + + // This method should NOT be discovered as a tool + public function internalHelperMethod(int $value): int + { + return $value * 2; + } + + #[McpTool(name: "private_tool_should_be_ignored")] // On private method + private function aPrivateTool(): void + { + } + + #[McpTool(name: "protected_tool_should_be_ignored")] // On protected method + protected function aProtectedTool(): void + { + } + + #[McpTool(name: "static_tool_should_be_ignored")] // On static method + public static function aStaticTool(): void + { + } +} diff --git a/vendor/php-mcp/server/tests/Fixtures/Discovery/EnhancedCompletionHandler.php b/vendor/php-mcp/server/tests/Fixtures/Discovery/EnhancedCompletionHandler.php new file mode 100644 index 000000000..00336de65 --- /dev/null +++ b/vendor/php-mcp/server/tests/Fixtures/Discovery/EnhancedCompletionHandler.php @@ -0,0 +1,50 @@ + 'user', 'content' => "Create a {$type} with status {$status} and priority {$priority}"] + ]; + } + + /** + * Resource template with list completion for categories. + */ + #[McpResourceTemplate( + uriTemplate: 'content://{category}/{slug}', + name: 'content_template' + )] + public function getContent( + #[CompletionProvider(values: ['news', 'blog', 'docs', 'api'])] + string $category, + string $slug + ): array { + return [ + 'category' => $category, + 'slug' => $slug, + 'url' => "https://example.com/{$category}/{$slug}" + ]; + } +} diff --git a/vendor/php-mcp/server/tests/Fixtures/Discovery/InvocablePromptFixture.php b/vendor/php-mcp/server/tests/Fixtures/Discovery/InvocablePromptFixture.php new file mode 100644 index 000000000..443e29264 --- /dev/null +++ b/vendor/php-mcp/server/tests/Fixtures/Discovery/InvocablePromptFixture.php @@ -0,0 +1,20 @@ + 'user', 'content' => "Generate a short greeting for {$personName}."]]; + } +} diff --git a/vendor/php-mcp/server/tests/Fixtures/Discovery/InvocableResourceFixture.php b/vendor/php-mcp/server/tests/Fixtures/Discovery/InvocableResourceFixture.php new file mode 100644 index 000000000..6c147797a --- /dev/null +++ b/vendor/php-mcp/server/tests/Fixtures/Discovery/InvocableResourceFixture.php @@ -0,0 +1,16 @@ + "OK", "load" => rand(1, 100) / 100.0]; + } +} diff --git a/vendor/php-mcp/server/tests/Fixtures/Discovery/InvocableResourceTemplateFixture.php b/vendor/php-mcp/server/tests/Fixtures/Discovery/InvocableResourceTemplateFixture.php new file mode 100644 index 000000000..2c70f8469 --- /dev/null +++ b/vendor/php-mcp/server/tests/Fixtures/Discovery/InvocableResourceTemplateFixture.php @@ -0,0 +1,20 @@ + $userId, "email" => "user{$userId}@example-invokable.com"]; + } +} diff --git a/vendor/php-mcp/server/tests/Fixtures/Discovery/InvocableToolFixture.php b/vendor/php-mcp/server/tests/Fixtures/Discovery/InvocableToolFixture.php new file mode 100644 index 000000000..c31871d3f --- /dev/null +++ b/vendor/php-mcp/server/tests/Fixtures/Discovery/InvocableToolFixture.php @@ -0,0 +1,25 @@ + str_starts_with($item, $currentValue)); + } +} diff --git a/vendor/php-mcp/server/tests/Fixtures/General/DocBlockTestFixture.php b/vendor/php-mcp/server/tests/Fixtures/General/DocBlockTestFixture.php new file mode 100644 index 000000000..5245bbc6e --- /dev/null +++ b/vendor/php-mcp/server/tests/Fixtures/General/DocBlockTestFixture.php @@ -0,0 +1,83 @@ + $param5 Array description. + * @param \stdClass $param6 Object param. + */ + public function methodWithParams(string $param1, ?int $param2, bool $param3, $param4, array $param5, \stdClass $param6): void + { + } + + /** + * Method with return tag. + * + * @return string The result of the operation. + */ + public function methodWithReturn(): string + { + return ''; + } + + /** + * Method with multiple tags. + * + * @param float $value The value to process. + * @return bool Status of the operation. + * @throws \RuntimeException If processing fails. + * @deprecated Use newMethod() instead. + * @see \PhpMcp\Server\Tests\Fixtures\General\DocBlockTestFixture::newMethod() + */ + public function methodWithMultipleTags(float $value): bool + { + return true; + } + + /** + * Malformed docblock - missing closing + */ + public function methodWithMalformedDocBlock(): void + { + } + + public function methodWithNoDocBlock(): void + { + } + + // Some other method needed for a @see tag perhaps + public function newMethod(): void + { + } +} diff --git a/vendor/php-mcp/server/tests/Fixtures/General/InvokableHandlerFixture.php b/vendor/php-mcp/server/tests/Fixtures/General/InvokableHandlerFixture.php new file mode 100644 index 000000000..ee3dfddda --- /dev/null +++ b/vendor/php-mcp/server/tests/Fixtures/General/InvokableHandlerFixture.php @@ -0,0 +1,20 @@ +type = $type; + } + + public function __invoke(string $arg1, int $arg2 = 0): array + { + $this->argsReceived = func_get_args(); + return ['invoked' => $this->type, 'arg1' => $arg1, 'arg2' => $arg2]; + } +} diff --git a/vendor/php-mcp/server/tests/Fixtures/General/PromptHandlerFixture.php b/vendor/php-mcp/server/tests/Fixtures/General/PromptHandlerFixture.php new file mode 100644 index 000000000..c95a7d489 --- /dev/null +++ b/vendor/php-mcp/server/tests/Fixtures/General/PromptHandlerFixture.php @@ -0,0 +1,154 @@ + 'user', 'content' => "Craft a {$style} greeting for {$name}."] + ]; + } + + public function returnSinglePromptMessageObject(): PromptMessage + { + return PromptMessage::make(Role::User, TextContent::make("Single PromptMessage object.")); + } + + public function returnArrayOfPromptMessageObjects(): array + { + return [ + PromptMessage::make(Role::User, TextContent::make("First message object.")), + PromptMessage::make(Role::Assistant, ImageContent::make("img_data", "image/png")), + ]; + } + + public function returnEmptyArrayForPrompt(): array + { + return []; + } + + public function returnSimpleUserAssistantMap(): array + { + return [ + 'user' => "This is the user's turn.", + 'assistant' => "And this is the assistant's reply." + ]; + } + + public function returnUserAssistantMapWithContentObjects(): array + { + return [ + 'user' => TextContent::make("User text content object."), + 'assistant' => ImageContent::make("asst_img_data", "image/gif"), + ]; + } + + public function returnUserAssistantMapWithMixedContent(): array + { + return [ + 'user' => "Plain user string.", + 'assistant' => AudioContent::make("aud_data", "audio/mp3"), + ]; + } + + public function returnUserAssistantMapWithArrayContent(): array + { + return [ + 'user' => ['type' => 'text', 'text' => 'User array content'], + 'assistant' => ['type' => 'image', 'data' => 'asst_arr_img_data', 'mimeType' => 'image/jpeg'], + ]; + } + + public function returnListOfRawMessageArrays(): array + { + return [ + ['role' => 'user', 'content' => "First raw message string."], + ['role' => 'assistant', 'content' => TextContent::make("Second raw message with Content obj.")], + ['role' => 'user', 'content' => ['type' => 'image', 'data' => 'raw_img_data', 'mimeType' => 'image/webp']], + ['role' => 'assistant', 'content' => ['type' => 'audio', 'data' => 'raw_aud_data', 'mimeType' => 'audio/ogg']], + ['role' => 'user', 'content' => ['type' => 'resource', 'resource' => ['uri' => 'file://doc.pdf', 'blob' => base64_encode('pdf-data'), 'mimeType' => 'application/pdf']]], + ['role' => 'assistant', 'content' => ['type' => 'resource', 'resource' => ['uri' => 'config://settings.json', 'text' => '{"theme":"dark"}']]], + ]; + } + + public function returnListOfRawMessageArraysWithScalars(): array + { + return [ + ['role' => 'user', 'content' => 123], // int + ['role' => 'assistant', 'content' => true], // bool + ['role' => 'user', 'content' => null], // null + ['role' => 'assistant', 'content' => 3.14], // float + ['role' => 'user', 'content' => ['key' => 'value']], // array that becomes JSON + ]; + } + + public function returnMixedArrayOfPromptMessagesAndRaw(): array + { + return [ + PromptMessage::make(Role::User, TextContent::make("This is a PromptMessage object.")), + ['role' => 'assistant', 'content' => "This is a raw message array."], + PromptMessage::make(Role::User, ImageContent::make("pm_img", "image/bmp")), + ['role' => 'assistant', 'content' => ['type' => 'text', 'text' => 'Raw message with typed content.']], + ]; + } + + public function promptWithArgumentCompletion( + #[CompletionProvider(provider: CompletionProviderFixture::class)] + string $entityName, + string $action = "describe" + ): array { + return [ + ['role' => 'user', 'content' => "Please {$action} the entity: {$entityName}."] + ]; + } + + public function promptReturnsNonArray(): string + { + return "This is not a valid prompt return type."; + } + + public function promptReturnsArrayWithInvalidRole(): array + { + return [['role' => 'system', 'content' => 'System messages are not directly supported.']]; + } + + public function promptReturnsInvalidRole(): array + { + return [['role' => 'system', 'content' => 'System messages are not directly supported.']]; + } + + public function promptReturnsArrayWithInvalidContentStructure(): array + { + return [['role' => 'user', 'content' => ['text_only_no_type' => 'invalid']]]; + } + + public function promptReturnsArrayWithInvalidTypedContent(): array + { + return [['role' => 'user', 'content' => ['type' => 'image', 'source' => 'url.jpg']]]; // 'image' needs 'data' and 'mimeType' + } + + public function promptReturnsArrayWithInvalidResourceContent(): array + { + return [ + [ + 'role' => 'user', + 'content' => ['type' => 'resource', 'resource' => ['uri' => 'uri://uri']] + ] + ]; + } + + public function promptHandlerThrows(): void + { + throw new \LogicException("Prompt generation failed inside handler."); + } +} diff --git a/vendor/php-mcp/server/tests/Fixtures/General/ResourceHandlerFixture.php b/vendor/php-mcp/server/tests/Fixtures/General/ResourceHandlerFixture.php new file mode 100644 index 000000000..94486ae93 --- /dev/null +++ b/vendor/php-mcp/server/tests/Fixtures/General/ResourceHandlerFixture.php @@ -0,0 +1,152 @@ +dynamicContentStore['dynamic://data/item1'] = "Content for item 1"; + } + + public function returnStringText(string $uri): string + { + return "Plain string content for {$uri}"; + } + + public function returnStringJson(string $uri): string + { + return json_encode(['uri_in_json' => $uri, 'data' => 'some json string']); + } + + public function returnStringHtml(string $uri): string + { + return "{$uri}Content"; + } + + public function returnArrayJson(string $uri): array + { + return ['uri_in_array' => $uri, 'message' => 'This is JSON data from array', 'timestamp' => time()]; + } + + public function returnEmptyArray(string $uri): array + { + return []; + } + + public function returnStream(string $uri) // Returns a stream resource + { + $stream = fopen('php://memory', 'r+'); + fwrite($stream, "Streamed content for {$uri}"); + rewind($stream); + return $stream; + } + + public function returnSplFileInfo(string $uri): SplFileInfo + { + self::$unlinkableSplFile = tempnam(sys_get_temp_dir(), 'res_fixture_spl_'); + file_put_contents(self::$unlinkableSplFile, "Content from SplFileInfo for {$uri}"); + return new SplFileInfo(self::$unlinkableSplFile); + } + + public function returnEmbeddedResource(string $uri): EmbeddedResource + { + return EmbeddedResource::make( + TextResourceContents::make($uri, 'application/vnd.custom-embedded', 'Direct EmbeddedResource content') + ); + } + + public function returnTextResourceContents(string $uri): TextResourceContents + { + return TextResourceContents::make($uri, 'text/special-contents', 'Direct TextResourceContents'); + } + + public function returnBlobResourceContents(string $uri): BlobResourceContents + { + return BlobResourceContents::make($uri, 'application/custom-blob-contents', base64_encode('blobbycontents')); + } + + public function returnArrayForBlobSchema(string $uri): array + { + return ['blob' => base64_encode("Blob for {$uri} via array"), 'mimeType' => 'application/x-custom-blob-array']; + } + + public function returnArrayForTextSchema(string $uri): array + { + return ['text' => "Text from array for {$uri} via array", 'mimeType' => 'text/vnd.custom-array-text']; + } + + public function returnArrayOfResourceContents(string $uri): array + { + return [ + TextResourceContents::make($uri . "_part1", 'text/plain', 'Part 1 of many RC'), + BlobResourceContents::make($uri . "_part2", 'image/png', base64_encode('pngdata')), + ]; + } + + public function returnArrayOfEmbeddedResources(string $uri): array + { + return [ + EmbeddedResource::make(TextResourceContents::make($uri . "_emb1", 'text/xml', '')), + EmbeddedResource::make(BlobResourceContents::make($uri . "_emb2", 'font/woff2', base64_encode('fontdata'))), + ]; + } + + public function returnMixedArrayWithResourceTypes(string $uri): array + { + return [ + "A raw string piece", // Will be formatted + TextResourceContents::make($uri . "_rc1", 'text/markdown', '**Markdown!**'), // Used as is + ['nested_array_data' => 'value', 'for_uri' => $uri], // Will be formatted (JSON) + EmbeddedResource::make(TextResourceContents::make($uri . "_emb1", 'text/csv', 'col1,col2')), // Extracted + ]; + } + + public function handlerThrowsException(string $uri): void + { + throw new \DomainException("Cannot read resource {$uri} - handler error."); + } + + public function returnUnformattableType(string $uri) + { + return new \DateTimeImmutable(); + } + + public function resourceHandlerNeedsUri(string $uri): string + { + return "Handler received URI: " . $uri; + } + + public function resourceHandlerDoesNotNeedUri(): string + { + return "Handler did not need or receive URI parameter."; + } + + public function getTemplatedContent( + string $category, + string $itemId, + string $format, + ): array { + return [ + 'message' => "Content for item {$itemId} in category {$category}, format {$format}.", + 'category_received' => $category, + 'itemId_received' => $itemId, + 'format_received' => $format, + ]; + } + + public function getStaticText(): string + { + return self::$staticTextContent; + } +} diff --git a/vendor/php-mcp/server/tests/Fixtures/General/ToolHandlerFixture.php b/vendor/php-mcp/server/tests/Fixtures/General/ToolHandlerFixture.php new file mode 100644 index 000000000..af3a9cc4e --- /dev/null +++ b/vendor/php-mcp/server/tests/Fixtures/General/ToolHandlerFixture.php @@ -0,0 +1,135 @@ + 'ok', 'timestamp' => time()]; + } + + public function processBackedEnum(BackedStringEnum $status): string + { + return "Status processed: " . $status->value; + } + + public function returnString(): string + { + return "This is a string result."; + } + + public function returnInteger(): int + { + return 12345; + } + + public function returnFloat(): float + { + return 67.89; + } + + public function returnBooleanTrue(): bool + { + return true; + } + + public function returnBooleanFalse(): bool + { + return false; + } + + public function returnNull(): ?string + { + return null; + } + + public function returnArray(): array + { + return ['message' => 'Array result', 'data' => [1, 2, 3]]; + } + + public function returnStdClass(): \stdClass + { + $obj = new \stdClass(); + $obj->property = "value"; + return $obj; + } + + public function returnTextContent(): TextContent + { + return TextContent::make("Pre-formatted TextContent."); + } + + public function returnImageContent(): ImageContent + { + return ImageContent::make("base64data==", "image/png"); + } + + public function returnAudioContent(): AudioContent + { + return AudioContent::make("base64audio==", "audio/mp3"); + } + + public function returnArrayOfContent(): array + { + return [ + TextContent::make("Part 1"), + ImageContent::make("imgdata", "image/jpeg") + ]; + } + + public function returnMixedArray(): array + { + return [ + "A raw string", + TextContent::make("A TextContent object"), + 123, + true, + null, + ['nested_key' => 'nested_value', 'sub_array' => [4, 5]], + ImageContent::make("img_data_mixed", "image/gif"), + (object)['obj_prop' => 'obj_val'] + ]; + } + + public function returnEmptyArray(): array + { + return []; + } + + public function toolThatThrows(): void + { + throw new \InvalidArgumentException("Something went wrong in the tool."); + } + + public function toolUnencodableResult() + { + return fopen('php://memory', 'r'); + } +} diff --git a/vendor/php-mcp/server/tests/Fixtures/General/VariousTypesHandler.php b/vendor/php-mcp/server/tests/Fixtures/General/VariousTypesHandler.php new file mode 100644 index 000000000..999dda929 --- /dev/null +++ b/vendor/php-mcp/server/tests/Fixtures/General/VariousTypesHandler.php @@ -0,0 +1,145 @@ + $pIntArrayGeneric Array of integers (generic style) + * @param array $pAssocArray Associative array + * @param BackedIntEnum[] $pEnumArray Array of enums + * @param array{name: string, age: int} $pShapeArray Typed array shape + * @param array $pArrayOfShapes Array of shapes + */ + public function arrayTypes( + array $pStringArray, + array $pIntArrayGeneric, + array $pAssocArray, + array $pEnumArray, + array $pShapeArray, + array $pArrayOfShapes + ): void { + } + + /** + * Enum types. + * @param BackedStringEnum $pBackedStringEnum Backed string enum + * @param BackedIntEnum $pBackedIntEnum Backed int enum + * @param UnitEnum $pUnitEnum Unit enum + */ + public function enumTypes(BackedStringEnum $pBackedStringEnum, BackedIntEnum $pBackedIntEnum, UnitEnum $pUnitEnum): void + { + } + + /** + * Variadic parameters. + * @param string ...$pVariadicStrings Variadic strings + */ + public function variadicParams(string ...$pVariadicStrings): void + { + } + + /** + * Mixed type. + * @param mixed $pMixed Mixed type + */ + public function mixedType(mixed $pMixed): void + { + } + + /** + * With #[Schema] attributes for enhanced validation. + * @param string $email With email format. + * @param int $quantity With numeric constraints. + * @param string[] $tags With array constraints. + * @param array $userProfile With object property constraints. + */ + public function withSchemaAttributes( + #[Schema(format: Format::EMAIL)] + string $email, + #[Schema(minimum: 1, maximum: 100, multipleOf: 5)] + int $quantity, + #[Schema(minItems: 1, maxItems: 5, uniqueItems: true, items: new ArrayItems(minLength: 3))] + array $tags, + #[Schema( + properties: [ + new Property(name: 'id', minimum: 1), + new Property(name: 'username', pattern: '^[a-z0-9_]{3,16}$'), + ], + required: ['id', 'username'], + additionalProperties: false + )] + array $userProfile + ): void { + } +} diff --git a/vendor/php-mcp/server/tests/Fixtures/ServerScripts/HttpTestServer.php b/vendor/php-mcp/server/tests/Fixtures/ServerScripts/HttpTestServer.php new file mode 100755 index 000000000..07d66ae72 --- /dev/null +++ b/vendor/php-mcp/server/tests/Fixtures/ServerScripts/HttpTestServer.php @@ -0,0 +1,46 @@ +#!/usr/bin/env php +withServerInfo('HttpIntegrationTestServer', '0.1.0') + ->withLogger($logger) + ->withTool([ToolHandlerFixture::class, 'greet'], 'greet_http_tool') + ->withResource([ResourceHandlerFixture::class, 'getStaticText'], "test://http/static", 'static_http_resource') + ->withPrompt([PromptHandlerFixture::class, 'generateSimpleGreeting'], 'simple_http_prompt') + ->build(); + + $transport = new HttpServerTransport($host, $port, $mcpPathPrefix); + $server->listen($transport); + + exit(0); +} catch (\Throwable $e) { + fwrite(STDERR, "[HTTP_SERVER_CRITICAL_ERROR]\nHost:{$host} Port:{$port} Prefix:{$mcpPathPrefix}\n" . $e->getMessage() . "\n" . $e->getTraceAsString() . "\n"); + exit(1); +} diff --git a/vendor/php-mcp/server/tests/Fixtures/ServerScripts/StdioTestServer.php b/vendor/php-mcp/server/tests/Fixtures/ServerScripts/StdioTestServer.php new file mode 100755 index 000000000..7651a9288 --- /dev/null +++ b/vendor/php-mcp/server/tests/Fixtures/ServerScripts/StdioTestServer.php @@ -0,0 +1,44 @@ +#!/usr/bin/env php +info('StdioTestServer listener starting.'); + + $server = Server::make() + ->withServerInfo('StdioIntegrationTestServer', '0.1.0') + ->withLogger($logger) + ->withTool([ToolHandlerFixture::class, 'greet'], 'greet_stdio_tool') + ->withResource([ResourceHandlerFixture::class, 'getStaticText'], 'test://stdio/static', 'static_stdio_resource') + ->withPrompt([PromptHandlerFixture::class, 'generateSimpleGreeting'], 'simple_stdio_prompt') + ->build(); + + $transport = new StdioServerTransport(); + $server->listen($transport); + + $logger->info('StdioTestServer listener stopped.'); + exit(0); +} catch (\Throwable $e) { + fwrite(STDERR, "[STDIO_SERVER_CRITICAL_ERROR]\n" . $e->getMessage() . "\n" . $e->getTraceAsString() . "\n"); + exit(1); +} diff --git a/vendor/php-mcp/server/tests/Fixtures/ServerScripts/StreamableHttpTestServer.php b/vendor/php-mcp/server/tests/Fixtures/ServerScripts/StreamableHttpTestServer.php new file mode 100755 index 000000000..07fba5c54 --- /dev/null +++ b/vendor/php-mcp/server/tests/Fixtures/ServerScripts/StreamableHttpTestServer.php @@ -0,0 +1,63 @@ +#!/usr/bin/env php +info("Starting StreamableHttpTestServer on {$host}:{$port}/{$mcpPath}, JSON Mode: " . ($enableJsonResponse ? 'ON' : 'OFF') . ", Stateless: " . ($stateless ? 'ON' : 'OFF')); + + $eventStore = $useEventStore ? new InMemoryEventStore() : null; + + $server = Server::make() + ->withServerInfo('StreamableHttpIntegrationServer', '0.1.0') + ->withLogger($logger) + ->withTool([ToolHandlerFixture::class, 'greet'], 'greet_streamable_tool') + ->withTool([ToolHandlerFixture::class, 'sum'], 'sum_streamable_tool') // For batch testing + ->withResource([ResourceHandlerFixture::class, 'getStaticText'], "test://streamable/static", 'static_streamable_resource') + ->withPrompt([PromptHandlerFixture::class, 'generateSimpleGreeting'], 'simple_streamable_prompt') + ->build(); + + $transport = new StreamableHttpServerTransport( + host: $host, + port: $port, + mcpPath: $mcpPath, + enableJsonResponse: $enableJsonResponse, + stateless: $stateless, + eventStore: $eventStore + ); + + $server->listen($transport); + + $logger->info("StreamableHttpTestServer listener stopped on {$host}:{$port}."); + exit(0); +} catch (\Throwable $e) { + fwrite(STDERR, "[STREAMABLE_HTTP_SERVER_CRITICAL_ERROR]\nHost:{$host} Port:{$port} Prefix:{$mcpPath}\n" . $e->getMessage() . "\n" . $e->getTraceAsString() . "\n"); + exit(1); +} diff --git a/vendor/php-mcp/server/tests/Fixtures/Utils/AttributeFixtures.php b/vendor/php-mcp/server/tests/Fixtures/Utils/AttributeFixtures.php new file mode 100644 index 000000000..c22994f5e --- /dev/null +++ b/vendor/php-mcp/server/tests/Fixtures/Utils/AttributeFixtures.php @@ -0,0 +1,56 @@ + generic syntax + * + * @param array $strings Array of strings using generic syntax + * @param array $integers Array of integers using generic syntax + * @param array $booleans Array of booleans using generic syntax + * @param array $floats Array of floats using generic syntax + * @param array $objects Array of objects using generic syntax + * @param array<\DateTime> $dateTimeInstances Array of DateTime objects using generic syntax + */ + public function genericArraySyntax( + array $strings, + array $integers, + array $booleans, + array $floats, + array $objects, + array $dateTimeInstances + ): void { + } + + /** + * Method with nested array syntax + * + * @param array> $nestedStringArrays Array of arrays of strings + * @param array> $nestedIntArrays Array of arrays of integers + * @param string[][] $doubleStringArrays Array of arrays of strings using double [] + * @param int[][] $doubleIntArrays Array of arrays of integers using double [] + */ + public function nestedArraySyntax( + array $nestedStringArrays, + array $nestedIntArrays, + array $doubleStringArrays, + array $doubleIntArrays + ): void { + } + + /** + * Method with object-like array syntax + * + * @param array{name: string, age: int} $person Simple object array with name and age + * @param array{id: int, title: string, tags: string[]} $article Article with array of tags + * @param array{user: array{id: int, name: string}, items: array} $order Order with nested user object and array of item IDs + */ + public function objectArraySyntax( + array $person, + array $article, + array $order + ): void { + } +} diff --git a/vendor/php-mcp/server/tests/Fixtures/Utils/SchemaGeneratorFixture.php b/vendor/php-mcp/server/tests/Fixtures/Utils/SchemaGeneratorFixture.php new file mode 100644 index 000000000..98b43b972 --- /dev/null +++ b/vendor/php-mcp/server/tests/Fixtures/Utils/SchemaGeneratorFixture.php @@ -0,0 +1,395 @@ + 'object', + 'description' => 'Creates a custom filter with complete definition', + 'properties' => [ + 'field' => ['type' => 'string', 'enum' => ['name', 'date', 'status']], + 'operator' => ['type' => 'string', 'enum' => ['eq', 'gt', 'lt', 'contains']], + 'value' => ['description' => 'Value to filter by, type depends on field and operator'] + ], + 'required' => ['field', 'operator', 'value'], + 'if' => [ + 'properties' => ['field' => ['const' => 'date']] + ], + 'then' => [ + 'properties' => ['value' => ['type' => 'string', 'format' => 'date']] + ] + ])] + public function methodLevelCompleteDefinition(string $field, string $operator, mixed $value): array + { + return compact('field', 'operator', 'value'); + } + + /** + * Method-level Schema defining properties. + */ + #[Schema( + description: "Creates a new user with detailed information.", + properties: [ + 'username' => ['type' => 'string', 'minLength' => 3, 'pattern' => '^[a-zA-Z0-9_]+$'], + 'email' => ['type' => 'string', 'format' => 'email'], + 'age' => ['type' => 'integer', 'minimum' => 18, 'description' => 'Age in years.'], + 'isActive' => ['type' => 'boolean', 'default' => true] + ], + required: ['username', 'email'] + )] + public function methodLevelWithProperties(string $username, string $email, int $age, bool $isActive = true): array + { + return compact('username', 'email', 'age', 'isActive'); + } + + /** + * Method-level Schema for complex array argument. + */ + #[Schema( + properties: [ + 'profiles' => [ + 'type' => 'array', + 'description' => 'An array of user profiles to update.', + 'minItems' => 1, + 'items' => [ + 'type' => 'object', + 'properties' => [ + 'id' => ['type' => 'integer'], + 'data' => ['type' => 'object', 'additionalProperties' => true] + ], + 'required' => ['id', 'data'] + ] + ] + ], + required: ['profiles'] + )] + public function methodLevelArrayArgument(array $profiles): array + { + return ['updated_count' => count($profiles)]; + } + + // ===== PARAMETER-LEVEL SCHEMA SCENARIOS ===== + + /** + * Parameter-level Schema attributes only. + */ + public function parameterLevelOnly( + #[Schema(description: "Recipient ID", pattern: "^user_")] + string $recipientId, + #[Schema(maxLength: 1024)] + string $messageBody, + #[Schema(type: 'integer', enum: [1, 2, 5])] + int $priority = 1, + #[Schema( + type: 'object', + properties: [ + 'type' => ['type' => 'string', 'enum' => ['sms', 'email', 'push']], + 'deviceToken' => ['type' => 'string', 'description' => 'Required if type is push'] + ], + required: ['type'] + )] + ?array $notificationConfig = null + ): array { + return compact('recipientId', 'messageBody', 'priority', 'notificationConfig'); + } + + /** + * Parameter-level Schema with string constraints. + */ + public function parameterStringConstraints( + #[Schema(format: 'email')] + string $email, + #[Schema(minLength: 8, pattern: '^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$')] + string $password, + string $regularString + ): void { + } + + /** + * Parameter-level Schema with numeric constraints. + */ + public function parameterNumericConstraints( + #[Schema(minimum: 18, maximum: 120)] + int $age, + #[Schema(minimum: 0, maximum: 5, exclusiveMaximum: true)] + float $rating, + #[Schema(multipleOf: 10)] + int $count + ): void { + } + + /** + * Parameter-level Schema with array constraints. + */ + public function parameterArrayConstraints( + #[Schema(type: 'array', items: ['type' => 'string'], minItems: 1, uniqueItems: true)] + array $tags, + #[Schema(type: 'array', items: ['type' => 'integer', 'minimum' => 0, 'maximum' => 100], minItems: 1, maxItems: 5)] + array $scores + ): void { + } + + // ===== COMBINED SCENARIOS ===== + + /** + * Method-level + Parameter-level Schema combination. + * @param string $settingKey The key of the setting + * @param mixed $newValue The new value for the setting + */ + #[Schema( + properties: [ + 'settingKey' => ['type' => 'string', 'description' => 'The key of the setting.'], + 'newValue' => ['description' => 'The new value for the setting (any type).'] + ], + required: ['settingKey', 'newValue'] + )] + public function methodAndParameterLevel( + string $settingKey, + #[Schema(description: "The specific new boolean value.", type: 'boolean')] + mixed $newValue + ): array { + return compact('settingKey', 'newValue'); + } + + /** + * Type hints + DocBlock + Parameter-level Schema. + * @param string $username The user's name + * @param int $priority Task priority level + */ + public function typeHintDocBlockAndParameterSchema( + #[Schema(minLength: 3, pattern: '^[a-zA-Z0-9_]+$')] + string $username, + #[Schema(minimum: 1, maximum: 10)] + int $priority + ): void { + } + + // ===== ENUM SCENARIOS ===== + + /** + * Various enum parameter types. + * @param BackedStringEnum $stringEnum Backed string enum + * @param BackedIntEnum $intEnum Backed int enum + * @param UnitEnum $unitEnum Unit enum + */ + public function enumParameters( + BackedStringEnum $stringEnum, + BackedIntEnum $intEnum, + UnitEnum $unitEnum, + ?BackedStringEnum $nullableEnum = null, + BackedIntEnum $enumWithDefault = BackedIntEnum::First + ): void { + } + + // ===== ARRAY TYPE SCENARIOS ===== + + /** + * Various array type scenarios. + * @param array $genericArray Generic array + * @param string[] $stringArray Array of strings + * @param int[] $intArray Array of integers + * @param array $mixedMap Mixed array map + * @param array{name: string, age: int} $objectLikeArray Object-like array + * @param array{user: array{id: int, name: string}, items: int[]} $nestedObjectArray Nested object array + */ + public function arrayTypeScenarios( + array $genericArray, + array $stringArray, + array $intArray, + array $mixedMap, + array $objectLikeArray, + array $nestedObjectArray + ): void { + } + + // ===== NULLABLE AND OPTIONAL SCENARIOS ===== + + /** + * Nullable and optional parameter scenarios. + * @param string|null $nullableString Nullable string + * @param int|null $nullableInt Nullable integer + */ + public function nullableAndOptional( + ?string $nullableString, + ?int $nullableInt = null, + string $optionalString = 'default', + bool $optionalBool = true, + array $optionalArray = [] + ): void { + } + + // ===== UNION TYPE SCENARIOS ===== + + /** + * Union type parameters. + * @param string|int $stringOrInt String or integer + * @param bool|string|null $multiUnion Bool, string or null + */ + public function unionTypes( + string|int $stringOrInt, + bool|string|null $multiUnion + ): void { + } + + // ===== VARIADIC SCENARIOS ===== + + /** + * Variadic parameter scenarios. + * @param string ...$items Variadic strings + */ + public function variadicStrings(string ...$items): void + { + } + + /** + * Variadic with Schema constraints. + * @param int ...$numbers Variadic integers + */ + public function variadicWithConstraints( + #[Schema(items: ['type' => 'integer', 'minimum' => 0])] + int ...$numbers + ): void { + } + + // ===== MIXED TYPE SCENARIOS ===== + + /** + * Mixed type scenarios. + * @param mixed $anyValue Any value + * @param mixed $optionalAny Optional any value + */ + public function mixedTypes( + mixed $anyValue, + mixed $optionalAny = 'default' + ): void { + } + + // ===== COMPLEX NESTED SCENARIOS ===== + + /** + * Complex nested Schema constraints. + */ + public function complexNestedSchema( + #[Schema( + type: 'object', + properties: [ + 'customer' => [ + 'type' => 'object', + 'properties' => [ + 'id' => ['type' => 'string', 'pattern' => '^CUS-[0-9]{6}$'], + 'name' => ['type' => 'string', 'minLength' => 2], + 'email' => ['type' => 'string', 'format' => 'email'] + ], + 'required' => ['id', 'name'] + ], + 'items' => [ + 'type' => 'array', + 'minItems' => 1, + 'items' => [ + 'type' => 'object', + 'properties' => [ + 'product_id' => ['type' => 'string', 'pattern' => '^PRD-[0-9]{4}$'], + 'quantity' => ['type' => 'integer', 'minimum' => 1], + 'price' => ['type' => 'number', 'minimum' => 0] + ], + 'required' => ['product_id', 'quantity', 'price'] + ] + ], + 'metadata' => [ + 'type' => 'object', + 'additionalProperties' => true + ] + ], + required: ['customer', 'items'] + )] + array $order + ): array { + return ['order_id' => uniqid()]; + } + + // ===== TYPE PRECEDENCE SCENARIOS ===== + + /** + * Testing type precedence between PHP, DocBlock, and Schema. + * @param integer $numericString DocBlock says integer despite string type hint + * @param string $stringWithConstraints String with Schema constraints + * @param array $arrayWithItems Array with Schema item overrides + */ + public function typePrecedenceTest( + string $numericString, + #[Schema(format: 'email', minLength: 5)] + string $stringWithConstraints, + #[Schema(items: ['type' => 'integer', 'minimum' => 1, 'maximum' => 100])] + array $arrayWithItems + ): void { + } + + // ===== ERROR EDGE CASES ===== + + /** + * Method with no parameters but Schema description. + */ + #[Schema(description: "Gets server status. Takes no arguments.", properties: [])] + public function noParamsWithSchema(): array + { + return ['status' => 'OK']; + } + + /** + * Parameter with Schema but inferred type. + */ + public function parameterSchemaInferredType( + #[Schema(description: "Some parameter", minLength: 3)] + $inferredParam + ): void { + } +} diff --git a/vendor/php-mcp/server/tests/Integration/DiscoveryTest.php b/vendor/php-mcp/server/tests/Integration/DiscoveryTest.php new file mode 100644 index 000000000..3fad529b4 --- /dev/null +++ b/vendor/php-mcp/server/tests/Integration/DiscoveryTest.php @@ -0,0 +1,174 @@ +registry = new Registry($logger); + + $docBlockParser = new DocBlockParser($logger); + $schemaGenerator = new SchemaGenerator($docBlockParser); + $this->discoverer = new Discoverer($this->registry, $logger, $docBlockParser, $schemaGenerator); + + $this->fixtureBasePath = realpath(__DIR__ . '/../Fixtures'); +}); + +it('discovers all element types correctly from fixture files', function () { + $scanDir = 'Discovery'; + + $this->discoverer->discover($this->fixtureBasePath, [$scanDir]); + + $tools = $this->registry->getTools(); + expect($tools)->toHaveCount(4); // greet_user, repeatAction, InvokableCalculator, hidden_subdir_tool + + $greetUserTool = $this->registry->getTool('greet_user'); + expect($greetUserTool)->toBeInstanceOf(RegisteredTool::class) + ->and($greetUserTool->isManual)->toBeFalse() + ->and($greetUserTool->schema->name)->toBe('greet_user') + ->and($greetUserTool->schema->description)->toBe('Greets a user by name.') + ->and($greetUserTool->handler)->toBe([DiscoverableToolHandler::class, 'greet']); + expect($greetUserTool->schema->inputSchema['properties'] ?? [])->toHaveKey('name'); + + $repeatActionTool = $this->registry->getTool('repeatAction'); + expect($repeatActionTool)->toBeInstanceOf(RegisteredTool::class) + ->and($repeatActionTool->isManual)->toBeFalse() + ->and($repeatActionTool->schema->description)->toBe('A tool with more complex parameters and inferred name/description.') + ->and($repeatActionTool->schema->annotations->readOnlyHint)->toBeTrue(); + expect(array_keys($repeatActionTool->schema->inputSchema['properties'] ?? []))->toEqual(['count', 'loudly', 'mode']); + + $invokableCalcTool = $this->registry->getTool('InvokableCalculator'); + expect($invokableCalcTool)->toBeInstanceOf(RegisteredTool::class) + ->and($invokableCalcTool->isManual)->toBeFalse() + ->and($invokableCalcTool->handler)->toBe([InvocableToolFixture::class, '__invoke']); + + expect($this->registry->getTool('private_tool_should_be_ignored'))->toBeNull(); + expect($this->registry->getTool('protected_tool_should_be_ignored'))->toBeNull(); + expect($this->registry->getTool('static_tool_should_be_ignored'))->toBeNull(); + + + $resources = $this->registry->getResources(); + expect($resources)->toHaveCount(3); // app_version, ui_settings_discovered, InvocableResourceFixture + + $appVersionRes = $this->registry->getResource('app://info/version'); + expect($appVersionRes)->toBeInstanceOf(RegisteredResource::class) + ->and($appVersionRes->isManual)->toBeFalse() + ->and($appVersionRes->schema->name)->toBe('app_version') + ->and($appVersionRes->schema->mimeType)->toBe('text/plain'); + + $invokableStatusRes = $this->registry->getResource('invokable://config/status'); + expect($invokableStatusRes)->toBeInstanceOf(RegisteredResource::class) + ->and($invokableStatusRes->isManual)->toBeFalse() + ->and($invokableStatusRes->handler)->toBe([InvocableResourceFixture::class, '__invoke']); + + + $prompts = $this->registry->getPrompts(); + expect($prompts)->toHaveCount(4); // creative_story_prompt, simpleQuestionPrompt, InvocablePromptFixture, content_creator + + $storyPrompt = $this->registry->getPrompt('creative_story_prompt'); + expect($storyPrompt)->toBeInstanceOf(RegisteredPrompt::class) + ->and($storyPrompt->isManual)->toBeFalse() + ->and($storyPrompt->schema->arguments)->toHaveCount(2) // genre, lengthWords + ->and($storyPrompt->completionProviders['genre'])->toBe(CompletionProviderFixture::class); + + $simplePrompt = $this->registry->getPrompt('simpleQuestionPrompt'); // Inferred name + expect($simplePrompt)->toBeInstanceOf(RegisteredPrompt::class) + ->and($simplePrompt->isManual)->toBeFalse(); + + $invokableGreeter = $this->registry->getPrompt('InvokableGreeterPrompt'); + expect($invokableGreeter)->toBeInstanceOf(RegisteredPrompt::class) + ->and($invokableGreeter->isManual)->toBeFalse() + ->and($invokableGreeter->handler)->toBe([InvocablePromptFixture::class, '__invoke']); + + $contentCreatorPrompt = $this->registry->getPrompt('content_creator'); + expect($contentCreatorPrompt)->toBeInstanceOf(RegisteredPrompt::class) + ->and($contentCreatorPrompt->isManual)->toBeFalse() + ->and($contentCreatorPrompt->completionProviders)->toHaveCount(3); + + $templates = $this->registry->getResourceTemplates(); + expect($templates)->toHaveCount(4); // product_details_template, getFileContent, InvocableResourceTemplateFixture, content_template + + $productTemplate = $this->registry->getResourceTemplate('product://{region}/details/{productId}'); + expect($productTemplate)->toBeInstanceOf(RegisteredResourceTemplate::class) + ->and($productTemplate->isManual)->toBeFalse() + ->and($productTemplate->schema->name)->toBe('product_details_template') + ->and($productTemplate->completionProviders['region'])->toBe(CompletionProviderFixture::class); + expect($productTemplate->getVariableNames())->toEqualCanonicalizing(['region', 'productId']); + + $invokableUserTemplate = $this->registry->getResourceTemplate('invokable://user-profile/{userId}'); + expect($invokableUserTemplate)->toBeInstanceOf(RegisteredResourceTemplate::class) + ->and($invokableUserTemplate->isManual)->toBeFalse() + ->and($invokableUserTemplate->handler)->toBe([InvocableResourceTemplateFixture::class, '__invoke']); +}); + +it('does not discover elements from excluded directories', function () { + $this->discoverer->discover($this->fixtureBasePath, ['Discovery']); + + expect($this->registry->getTool('hidden_subdir_tool'))->not->toBeNull(); + + $this->registry->clear(); + + $this->discoverer->discover($this->fixtureBasePath, ['Discovery'], ['SubDir']); + expect($this->registry->getTool('hidden_subdir_tool'))->toBeNull(); +}); + +it('handles empty directories or directories with no PHP files', function () { + $this->discoverer->discover($this->fixtureBasePath, ['EmptyDir']); + expect($this->registry->getTools())->toBeEmpty(); +}); + +it('correctly infers names and descriptions from methods/classes if not set in attribute', function () { + $this->discoverer->discover($this->fixtureBasePath, ['Discovery']); + + $repeatActionTool = $this->registry->getTool('repeatAction'); + expect($repeatActionTool->schema->name)->toBe('repeatAction'); // Method name + expect($repeatActionTool->schema->description)->toBe('A tool with more complex parameters and inferred name/description.'); // Docblock summary + + $simplePrompt = $this->registry->getPrompt('simpleQuestionPrompt'); + expect($simplePrompt->schema->name)->toBe('simpleQuestionPrompt'); + expect($simplePrompt->schema->description)->toBeNull(); + + $invokableCalc = $this->registry->getTool('InvokableCalculator'); // Name comes from Attr + expect($invokableCalc->schema->name)->toBe('InvokableCalculator'); + expect($invokableCalc->schema->description)->toBe('An invokable calculator tool.'); +}); + +it('discovers enhanced completion providers with values and enum attributes', function () { + $this->discoverer->discover($this->fixtureBasePath, ['Discovery']); + + $contentPrompt = $this->registry->getPrompt('content_creator'); + expect($contentPrompt)->toBeInstanceOf(RegisteredPrompt::class); + + expect($contentPrompt->completionProviders)->toHaveCount(3); + + $typeProvider = $contentPrompt->completionProviders['type']; + expect($typeProvider)->toBeInstanceOf(ListCompletionProvider::class); + + $statusProvider = $contentPrompt->completionProviders['status']; + expect($statusProvider)->toBeInstanceOf(EnumCompletionProvider::class); + + $priorityProvider = $contentPrompt->completionProviders['priority']; + expect($priorityProvider)->toBeInstanceOf(EnumCompletionProvider::class); + + $contentTemplate = $this->registry->getResourceTemplate('content://{category}/{slug}'); + expect($contentTemplate)->toBeInstanceOf(RegisteredResourceTemplate::class); + expect($contentTemplate->completionProviders)->toHaveCount(1); + + $categoryProvider = $contentTemplate->completionProviders['category']; + expect($categoryProvider)->toBeInstanceOf(ListCompletionProvider::class); +}); diff --git a/vendor/php-mcp/server/tests/Integration/HttpServerTransportTest.php b/vendor/php-mcp/server/tests/Integration/HttpServerTransportTest.php new file mode 100644 index 000000000..dab32bd93 --- /dev/null +++ b/vendor/php-mcp/server/tests/Integration/HttpServerTransportTest.php @@ -0,0 +1,419 @@ +loop = Loop::get(); + $this->port = findFreePort(); + + if (!is_file(HTTP_SERVER_SCRIPT_PATH)) { + $this->markTestSkipped("Server script not found: " . HTTP_SERVER_SCRIPT_PATH); + } + if (!is_executable(HTTP_SERVER_SCRIPT_PATH)) { + chmod(HTTP_SERVER_SCRIPT_PATH, 0755); + } + + $phpPath = PHP_BINARY ?: 'php'; + $commandPhpPath = str_contains($phpPath, ' ') ? '"' . $phpPath . '"' : $phpPath; + $commandArgs = [ + escapeshellarg(HTTP_SERVER_HOST), + escapeshellarg((string)$this->port), + escapeshellarg(HTTP_MCP_PATH_PREFIX) + ]; + $commandScriptPath = escapeshellarg(HTTP_SERVER_SCRIPT_PATH); + $command = $commandPhpPath . ' ' . $commandScriptPath . ' ' . implode(' ', $commandArgs); + + $this->process = new Process($command, getcwd() ?: null, null, []); + $this->process->start($this->loop); + + $this->processErrorOutput = ''; + if ($this->process->stderr instanceof ReadableStreamInterface) { + $this->process->stderr->on('data', function ($chunk) { + $this->processErrorOutput .= $chunk; + }); + } + + return await(delay(0.2, $this->loop)); +}); + +afterEach(function () { + if ($this->sseClient ?? null) { + $this->sseClient->close(); + } + + if ($this->process instanceof Process && $this->process->isRunning()) { + if ($this->process->stdout instanceof ReadableStreamInterface) { + $this->process->stdout->close(); + } + if ($this->process->stderr instanceof ReadableStreamInterface) { + $this->process->stderr->close(); + } + + $this->process->terminate(SIGTERM); + try { + await(delay(0.02, $this->loop)); + } catch (\Throwable $e) { + } + + if ($this->process->isRunning()) { + $this->process->terminate(SIGKILL); + } + } + $this->process = null; +}); + +afterAll(function () { + // Loop::stop(); +}); + +it('starts the http server, initializes, calls a tool, and closes', function () { + $this->sseClient = new MockSseClient(); + $sseBaseUrl = "http://" . HTTP_SERVER_HOST . ":" . $this->port . "/" . HTTP_MCP_PATH_PREFIX . "/sse"; + + // 1. Connect + await($this->sseClient->connect($sseBaseUrl)); + await(delay(0.05, $this->loop)); + expect($this->sseClient->endpointUrl)->toBeString(); + expect($this->sseClient->clientId)->toBeString(); + + // 2. Initialize Request + await($this->sseClient->sendHttpRequest('init-http-1', 'initialize', [ + 'protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, + 'clientInfo' => ['name' => 'HttpPestClient', 'version' => '1.0'], + 'capabilities' => [] + ])); + $initResponse = await($this->sseClient->getNextMessageResponse('init-http-1')); + + expect($initResponse['id'])->toBe('init-http-1'); + expect($initResponse)->not->toHaveKey('error'); + expect($initResponse['result']['protocolVersion'])->toBe(Protocol::LATEST_PROTOCOL_VERSION); + expect($initResponse['result']['serverInfo']['name'])->toBe('HttpIntegrationTestServer'); + + // 3. Initialized Notification + await($this->sseClient->sendHttpNotification('notifications/initialized', ['messageQueueSupported' => true])); + await(delay(0.05, $this->loop)); + + // 4. Call a tool + await($this->sseClient->sendHttpRequest('tool-http-1', 'tools/call', [ + 'name' => 'greet_http_tool', + 'arguments' => ['name' => 'HTTP Integration User'] + ])); + $toolResponse = await($this->sseClient->getNextMessageResponse('tool-http-1')); + + expect($toolResponse['id'])->toBe('tool-http-1'); + expect($toolResponse)->not->toHaveKey('error'); + expect($toolResponse['result']['content'][0]['text'])->toBe('Hello, HTTP Integration User!'); + expect($toolResponse['result']['isError'])->toBeFalse(); + + // 5. Close + $this->sseClient->close(); +})->group('integration', 'http_transport'); + +it('can handle invalid JSON from client', function () { + $this->sseClient = new MockSseClient(); + $sseBaseUrl = "http://" . HTTP_SERVER_HOST . ":" . $this->port . "/" . HTTP_MCP_PATH_PREFIX . "/sse"; + + // 1. Connect + await($this->sseClient->connect($sseBaseUrl)); + await(delay(0.05, $this->loop)); + + expect($this->sseClient->endpointUrl)->toBeString(); + + $malformedJson = '{"jsonrpc":"2.0", "id": "bad-json-1", "method": "tools/list", "params": {"broken"}'; + + // 2. Send invalid JSON + $postPromise = $this->sseClient->browser->post( + $this->sseClient->endpointUrl, + ['Content-Type' => 'application/json'], + $malformedJson + ); + + // 3. Expect error response + try { + await(timeout($postPromise, HTTP_PROCESS_TIMEOUT_SECONDS - 2, $this->loop)); + } catch (ResponseException $e) { + expect($e->getResponse()->getStatusCode())->toBe(400); + + $errorResponse = json_decode($e->getResponse()->getBody(), true); + expect($errorResponse['jsonrpc'])->toBe('2.0'); + expect($errorResponse['id'])->toBe(''); + expect($errorResponse['error']['code'])->toBe(-32700); + expect($errorResponse['error']['message'])->toContain('Invalid JSON-RPC message'); + } +})->group('integration', 'http_transport'); + +it('can handle request for non-existent method after initialization', function () { + $this->sseClient = new MockSseClient(); + $sseBaseUrl = "http://" . HTTP_SERVER_HOST . ":" . $this->port . "/" . HTTP_MCP_PATH_PREFIX . "/sse"; + + // 1. Connect + await($this->sseClient->connect($sseBaseUrl)); + await(delay(0.05, $this->loop)); + expect($this->sseClient->endpointUrl)->toBeString(); + + // 2. Initialize Request + await($this->sseClient->sendHttpRequest('init-http-nonexist', 'initialize', [ + 'protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, + 'clientInfo' => ['name' => 'Test'], + 'capabilities' => [] + ])); + await($this->sseClient->getNextMessageResponse('init-http-nonexist')); + await($this->sseClient->sendHttpNotification('notifications/initialized', ['messageQueueSupported' => true])); + await(delay(0.05, $this->loop)); + + // 3. Send request for non-existent method + await($this->sseClient->sendHttpRequest('err-meth-http-1', 'non/existentHttpTool', [])); + $errorResponse = await($this->sseClient->getNextMessageResponse('err-meth-http-1')); + + // 4. Expect error response + expect($errorResponse['id'])->toBe('err-meth-http-1'); + expect($errorResponse['error']['code'])->toBe(-32601); + expect($errorResponse['error']['message'])->toContain("Method 'non/existentHttpTool' not found"); +})->group('integration', 'http_transport'); + +it('can handle batch requests correctly over HTTP/SSE', function () { + $this->sseClient = new MockSseClient(); + $sseBaseUrl = "http://" . HTTP_SERVER_HOST . ":" . $this->port . "/" . HTTP_MCP_PATH_PREFIX . "/sse"; + + // 1. Connect + await($this->sseClient->connect($sseBaseUrl)); + await(delay(0.05, $this->loop)); + expect($this->sseClient->endpointUrl)->toBeString(); + + // 2. Initialize Request + await($this->sseClient->sendHttpRequest('init-batch-http', 'initialize', [ + 'protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, + 'clientInfo' => ['name' => 'HttpBatchClient', 'version' => '1.0'], + 'capabilities' => [] + ])); + await($this->sseClient->getNextMessageResponse('init-batch-http')); + + // 3. Initialized notification + await($this->sseClient->sendHttpNotification('notifications/initialized', ['messageQueueSupported' => true])); + await(delay(0.05, $this->loop)); + + // 4. Send Batch Request + $batchRequests = [ + ['jsonrpc' => '2.0', 'id' => 'batch-req-1', 'method' => 'tools/call', 'params' => ['name' => 'greet_http_tool', 'arguments' => ['name' => 'Batch Item 1']]], + ['jsonrpc' => '2.0', 'method' => 'notifications/something'], + ['jsonrpc' => '2.0', 'id' => 'batch-req-2', 'method' => 'tools/call', 'params' => ['name' => 'greet_http_tool', 'arguments' => ['name' => 'Batch Item 2']]], + ['jsonrpc' => '2.0', 'id' => 'batch-req-3', 'method' => 'nonexistent/method'] + ]; + + await($this->sseClient->sendHttpBatchRequest($batchRequests)); + + // 5. Read Batch Response + $batchResponseArray = await($this->sseClient->getNextBatchMessageResponse(3)); + + expect($batchResponseArray)->toBeArray()->toHaveCount(3); + + $findResponseById = function (array $batch, $id) { + foreach ($batch as $item) { + if (isset($item['id']) && $item['id'] === $id) { + return $item; + } + } + return null; + }; + + $response1 = $findResponseById($batchResponseArray, 'batch-req-1'); + $response2 = $findResponseById($batchResponseArray, 'batch-req-2'); + $response3 = $findResponseById($batchResponseArray, 'batch-req-3'); + + expect($response1['result']['content'][0]['text'])->toBe('Hello, Batch Item 1!'); + expect($response2['result']['content'][0]['text'])->toBe('Hello, Batch Item 2!'); + expect($response3['error']['code'])->toBe(-32601); + expect($response3['error']['message'])->toContain("Method 'nonexistent/method' not found"); + + $this->sseClient->close(); +})->group('integration', 'http_transport'); + +it('can handle tool list request over HTTP/SSE', function () { + $this->sseClient = new MockSseClient(); + $sseBaseUrl = "http://" . HTTP_SERVER_HOST . ":" . $this->port . "/" . HTTP_MCP_PATH_PREFIX . "/sse"; + await($this->sseClient->connect($sseBaseUrl)); + await(delay(0.05, $this->loop)); + + await($this->sseClient->sendHttpRequest('init-http-tools', 'initialize', ['protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, 'clientInfo' => [], 'capabilities' => []])); + await($this->sseClient->getNextMessageResponse('init-http-tools')); + await($this->sseClient->sendHttpNotification('notifications/initialized')); + await(delay(0.1, $this->loop)); + + await($this->sseClient->sendHttpRequest('tool-list-http-1', 'tools/list', [])); + $toolListResponse = await($this->sseClient->getNextMessageResponse('tool-list-http-1')); + + expect($toolListResponse['id'])->toBe('tool-list-http-1'); + expect($toolListResponse)->not->toHaveKey('error'); + expect($toolListResponse['result']['tools'])->toBeArray()->toHaveCount(1); + expect($toolListResponse['result']['tools'][0]['name'])->toBe('greet_http_tool'); + + $this->sseClient->close(); +})->group('integration', 'http_transport'); + +it('can read a registered resource over HTTP/SSE', function () { + $this->sseClient = new MockSseClient(); + $sseBaseUrl = "http://" . HTTP_SERVER_HOST . ":" . $this->port . "/" . HTTP_MCP_PATH_PREFIX . "/sse"; + await($this->sseClient->connect($sseBaseUrl)); + await(delay(0.05, $this->loop)); + + await($this->sseClient->sendHttpRequest('init-http-res', 'initialize', ['protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, 'clientInfo' => [], 'capabilities' => []])); + await($this->sseClient->getNextMessageResponse('init-http-res')); + await($this->sseClient->sendHttpNotification('notifications/initialized')); + await(delay(0.1, $this->loop)); + + await($this->sseClient->sendHttpRequest('res-read-http-1', 'resources/read', ['uri' => 'test://http/static'])); + $resourceResponse = await($this->sseClient->getNextMessageResponse('res-read-http-1')); + + expect($resourceResponse['id'])->toBe('res-read-http-1'); + expect($resourceResponse)->not->toHaveKey('error'); + expect($resourceResponse['result']['contents'])->toBeArray()->toHaveCount(1); + expect($resourceResponse['result']['contents'][0]['uri'])->toBe('test://http/static'); + expect($resourceResponse['result']['contents'][0]['text'])->toBe(ResourceHandlerFixture::$staticTextContent); + expect($resourceResponse['result']['contents'][0]['mimeType'])->toBe('text/plain'); + + $this->sseClient->close(); +})->group('integration', 'http_transport'); + +it('can get a registered prompt over HTTP/SSE', function () { + $this->sseClient = new MockSseClient(); + $sseBaseUrl = "http://" . HTTP_SERVER_HOST . ":" . $this->port . "/" . HTTP_MCP_PATH_PREFIX . "/sse"; + await($this->sseClient->connect($sseBaseUrl)); + await(delay(0.05, $this->loop)); + + await($this->sseClient->sendHttpRequest('init-http-prompt', 'initialize', ['protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, 'clientInfo' => [], 'capabilities' => []])); + await($this->sseClient->getNextMessageResponse('init-http-prompt')); + await($this->sseClient->sendHttpNotification('notifications/initialized')); + await(delay(0.1, $this->loop)); + + await($this->sseClient->sendHttpRequest('prompt-get-http-1', 'prompts/get', [ + 'name' => 'simple_http_prompt', + 'arguments' => ['name' => 'HttpPromptUser', 'style' => 'polite'] + ])); + $promptResponse = await($this->sseClient->getNextMessageResponse('prompt-get-http-1')); + + expect($promptResponse['id'])->toBe('prompt-get-http-1'); + expect($promptResponse)->not->toHaveKey('error'); + expect($promptResponse['result']['messages'])->toBeArray()->toHaveCount(1); + expect($promptResponse['result']['messages'][0]['role'])->toBe('user'); + expect($promptResponse['result']['messages'][0]['content']['text'])->toBe('Craft a polite greeting for HttpPromptUser.'); + + $this->sseClient->close(); +})->group('integration', 'http_transport'); + +it('rejects subsequent requests if client does not send initialized notification', function () { + $this->sseClient = new MockSseClient(); + $sseBaseUrl = "http://" . HTTP_SERVER_HOST . ":" . $this->port . "/" . HTTP_MCP_PATH_PREFIX . "/sse"; + await($this->sseClient->connect($sseBaseUrl)); + await(delay(0.05, $this->loop)); + + // 1. Send Initialize + await($this->sseClient->sendHttpRequest('init-http-no-ack', 'initialize', [ + 'protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, + 'clientInfo' => ['name' => 'HttpForgetfulClient', 'version' => '1.0'], + 'capabilities' => [] + ])); + await($this->sseClient->getNextMessageResponse('init-http-no-ack')); + // Client "forgets" to send notifications/initialized back + + await(delay(0.1, $this->loop)); + + // 2. Attempt to Call a tool + await($this->sseClient->sendHttpRequest('tool-call-http-no-ack', 'tools/call', [ + 'name' => 'greet_http_tool', + 'arguments' => ['name' => 'NoAckHttpUser'] + ])); + $toolResponse = await($this->sseClient->getNextMessageResponse('tool-call-http-no-ack')); + + expect($toolResponse['id'])->toBe('tool-call-http-no-ack'); + expect($toolResponse['error']['code'])->toBe(-32600); // Invalid Request + expect($toolResponse['error']['message'])->toContain('Client session not initialized'); + + $this->sseClient->close(); +})->group('integration', 'http_transport'); + +it('returns 404 for POST to /message without valid clientId in query', function () { + $this->sseClient = new MockSseClient(); + $sseBaseUrl = "http://" . HTTP_SERVER_HOST . ":" . $this->port . "/" . HTTP_MCP_PATH_PREFIX . "/sse"; + await($this->sseClient->connect($sseBaseUrl)); + await(delay(0.05, $this->loop)); + $validEndpointUrl = $this->sseClient->endpointUrl; + $this->sseClient->close(); + + $malformedEndpoint = (string) (new Uri($validEndpointUrl))->withQuery(''); + + $payload = ['jsonrpc' => '2.0', 'id' => 'post-no-clientid', 'method' => 'ping', 'params' => []]; + $postPromise = $this->sseClient->browser->post( + $malformedEndpoint, + ['Content-Type' => 'application/json'], + json_encode($payload) + ); + + try { + await(timeout($postPromise, HTTP_PROCESS_TIMEOUT_SECONDS - 2, $this->loop)); + } catch (ResponseException $e) { + expect($e->getResponse()->getStatusCode())->toBe(400); + $bodyContent = (string) $e->getResponse()->getBody(); + $errorData = json_decode($bodyContent, true); + expect($errorData['error']['message'])->toContain('Missing or invalid clientId'); + } +})->group('integration', 'http_transport'); + +it('returns 404 for POST to /message with clientId for a disconnected SSE stream', function () { + $this->sseClient = new MockSseClient(); + $sseBaseUrl = "http://" . HTTP_SERVER_HOST . ":" . $this->port . "/" . HTTP_MCP_PATH_PREFIX . "/sse"; + + await($this->sseClient->connect($sseBaseUrl)); + await(delay(0.05, $this->loop)); + $originalEndpointUrl = $this->sseClient->endpointUrl; + $this->sseClient->close(); + + await(delay(0.1, $this->loop)); + + $payload = ['jsonrpc' => '2.0', 'id' => 'post-stale-clientid', 'method' => 'ping', 'params' => []]; + $postPromise = $this->sseClient->browser->post( + $originalEndpointUrl, + ['Content-Type' => 'application/json'], + json_encode($payload) + ); + + try { + await(timeout($postPromise, HTTP_PROCESS_TIMEOUT_SECONDS - 2, $this->loop)); + } catch (ResponseException $e) { + $bodyContent = (string) $e->getResponse()->getBody(); + $errorData = json_decode($bodyContent, true); + expect($errorData['error']['message'])->toContain('Session ID not found or disconnected'); + } +})->group('integration', 'http_transport'); + +it('returns 404 for unknown paths', function () { + $browser = new Browser($this->loop); + $unknownUrl = "http://" . HTTP_SERVER_HOST . ":" . $this->port . "/unknown/path"; + + $promise = $browser->get($unknownUrl); + + try { + await(timeout($promise, HTTP_PROCESS_TIMEOUT_SECONDS - 2, $this->loop)); + $this->fail("Request to unknown path should have failed with 404."); + } catch (ResponseException $e) { + expect($e->getResponse()->getStatusCode())->toBe(404); + $body = (string) $e->getResponse()->getBody(); + expect($body)->toContain("Not Found"); + } catch (\Throwable $e) { + $this->fail("Request to unknown path failed with unexpected error: " . $e->getMessage()); + } +})->group('integration', 'http_transport'); diff --git a/vendor/php-mcp/server/tests/Integration/SchemaGenerationTest.php b/vendor/php-mcp/server/tests/Integration/SchemaGenerationTest.php new file mode 100644 index 000000000..1069c014f --- /dev/null +++ b/vendor/php-mcp/server/tests/Integration/SchemaGenerationTest.php @@ -0,0 +1,346 @@ +schemaGenerator = new SchemaGenerator($docBlockParser); +}); + +it('generates an empty properties object for a method with no parameters', function () { + $method = new ReflectionMethod(SchemaGeneratorFixture::class, 'noParams'); + $schema = $this->schemaGenerator->generate($method); + + expect($schema)->toEqual([ + 'type' => 'object', + 'properties' => new stdClass() + ]); + expect($schema)->not->toHaveKey('required'); +}); + +it('infers basic types from PHP type hints when no DocBlocks or Schema attributes are present', function () { + $method = new ReflectionMethod(SchemaGeneratorFixture::class, 'typeHintsOnly'); + $schema = $this->schemaGenerator->generate($method); + + expect($schema['properties']['name'])->toEqual(['type' => 'string']); + expect($schema['properties']['age'])->toEqual(['type' => 'integer']); + expect($schema['properties']['active'])->toEqual(['type' => 'boolean']); + expect($schema['properties']['tags'])->toEqual(['type' => 'array']); + expect($schema['properties']['config'])->toEqual(['type' => ['null', 'object'], 'default' => null]); + + expect($schema['required'])->toEqualCanonicalizing(['name', 'age', 'active', 'tags']); +}); + +it('infers types and descriptions from DocBlock @param tags when no PHP type hints are present', function () { + $method = new ReflectionMethod(SchemaGeneratorFixture::class, 'docBlockOnly'); + $schema = $this->schemaGenerator->generate($method); + + expect($schema['properties']['username'])->toEqual(['type' => 'string', 'description' => 'The username']); + expect($schema['properties']['count'])->toEqual(['type' => 'integer', 'description' => 'Number of items']); + expect($schema['properties']['enabled'])->toEqual(['type' => 'boolean', 'description' => 'Whether enabled']); + expect($schema['properties']['data'])->toEqual(['type' => 'array', 'description' => 'Some data']); + + expect($schema['required'])->toEqualCanonicalizing(['username', 'count', 'enabled', 'data']); +}); + +it('uses PHP type hints for type and DocBlock @param tags for descriptions when both are present', function () { + $method = new ReflectionMethod(SchemaGeneratorFixture::class, 'typeHintsWithDocBlock'); + $schema = $this->schemaGenerator->generate($method); + + expect($schema['properties']['email'])->toEqual(['type' => 'string', 'description' => 'User email address']); + expect($schema['properties']['score'])->toEqual(['type' => 'integer', 'description' => 'User score']); + expect($schema['properties']['verified'])->toEqual(['type' => 'boolean', 'description' => 'Whether user is verified']); + + expect($schema['required'])->toEqualCanonicalizing(['email', 'score', 'verified']); +}); + +it('uses the complete schema definition provided by a method-level #[Schema(definition: ...)] attribute', function () { + $method = new ReflectionMethod(SchemaGeneratorFixture::class, 'methodLevelCompleteDefinition'); + $schema = $this->schemaGenerator->generate($method); + + // Should return the complete definition as-is + expect($schema)->toEqual([ + 'type' => 'object', + 'description' => 'Creates a custom filter with complete definition', + 'properties' => [ + 'field' => ['type' => 'string', 'enum' => ['name', 'date', 'status']], + 'operator' => ['type' => 'string', 'enum' => ['eq', 'gt', 'lt', 'contains']], + 'value' => ['description' => 'Value to filter by, type depends on field and operator'] + ], + 'required' => ['field', 'operator', 'value'], + 'if' => [ + 'properties' => ['field' => ['const' => 'date']] + ], + 'then' => [ + 'properties' => ['value' => ['type' => 'string', 'format' => 'date']] + ] + ]); +}); + +it('generates schema from a method-level #[Schema] attribute defining properties for each parameter', function () { + $method = new ReflectionMethod(SchemaGeneratorFixture::class, 'methodLevelWithProperties'); + $schema = $this->schemaGenerator->generate($method); + + expect($schema['description'])->toBe("Creates a new user with detailed information."); + expect($schema['properties']['username'])->toEqual(['type' => 'string', 'minLength' => 3, 'pattern' => '^[a-zA-Z0-9_]+$']); + expect($schema['properties']['email'])->toEqual(['type' => 'string', 'format' => 'email']); + expect($schema['properties']['age'])->toEqual(['type' => 'integer', 'minimum' => 18, 'description' => 'Age in years.']); + expect($schema['properties']['isActive'])->toEqual(['type' => 'boolean', 'default' => true]); + + expect($schema['required'])->toEqualCanonicalizing(['age', 'username', 'email']); +}); + +it('generates schema for a single array argument defined by a method-level #[Schema] attribute', function () { + $method = new ReflectionMethod(SchemaGeneratorFixture::class, 'methodLevelArrayArgument'); + $schema = $this->schemaGenerator->generate($method); + + expect($schema['properties']['profiles'])->toEqual([ + 'type' => 'array', + 'description' => 'An array of user profiles to update.', + 'minItems' => 1, + 'items' => [ + 'type' => 'object', + 'properties' => [ + 'id' => ['type' => 'integer'], + 'data' => ['type' => 'object', 'additionalProperties' => true] + ], + 'required' => ['id', 'data'] + ] + ]); + + expect($schema['required'])->toEqual(['profiles']); +}); + +it('generates schema from individual parameter-level #[Schema] attributes', function () { + $method = new ReflectionMethod(SchemaGeneratorFixture::class, 'parameterLevelOnly'); + $schema = $this->schemaGenerator->generate($method); + + expect($schema['properties']['recipientId'])->toEqual(['description' => "Recipient ID", 'pattern' => "^user_", 'type' => 'string']); + expect($schema['properties']['messageBody'])->toEqual(['maxLength' => 1024, 'type' => 'string']); + expect($schema['properties']['priority'])->toEqual(['type' => 'integer', 'enum' => [1, 2, 5], 'default' => 1]); + expect($schema['properties']['notificationConfig'])->toEqual([ + 'type' => 'object', + 'properties' => [ + 'type' => ['type' => 'string', 'enum' => ['sms', 'email', 'push']], + 'deviceToken' => ['type' => 'string', 'description' => 'Required if type is push'] + ], + 'required' => ['type'], + 'default' => null + ]); + + expect($schema['required'])->toEqualCanonicalizing(['recipientId', 'messageBody']); +}); + +it('applies string constraints from parameter-level #[Schema] attributes', function () { + $method = new ReflectionMethod(SchemaGeneratorFixture::class, 'parameterStringConstraints'); + $schema = $this->schemaGenerator->generate($method); + + expect($schema['properties']['email'])->toEqual(['format' => 'email', 'type' => 'string']); + expect($schema['properties']['password'])->toEqual(['minLength' => 8, 'pattern' => '^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$', 'type' => 'string']); + expect($schema['properties']['regularString'])->toEqual(['type' => 'string']); + + expect($schema['required'])->toEqualCanonicalizing(['email', 'password', 'regularString']); +}); + +it('applies numeric constraints from parameter-level #[Schema] attributes', function () { + $method = new ReflectionMethod(SchemaGeneratorFixture::class, 'parameterNumericConstraints'); + $schema = $this->schemaGenerator->generate($method); + + expect($schema['properties']['age'])->toEqual(['minimum' => 18, 'maximum' => 120, 'type' => 'integer']); + expect($schema['properties']['rating'])->toEqual(['minimum' => 0, 'maximum' => 5, 'exclusiveMaximum' => true, 'type' => 'number']); + expect($schema['properties']['count'])->toEqual(['multipleOf' => 10, 'type' => 'integer']); + + expect($schema['required'])->toEqualCanonicalizing(['age', 'rating', 'count']); +}); + +it('applies array constraints (minItems, uniqueItems, items schema) from parameter-level #[Schema]', function () { + $method = new ReflectionMethod(SchemaGeneratorFixture::class, 'parameterArrayConstraints'); + $schema = $this->schemaGenerator->generate($method); + + expect($schema['properties']['tags'])->toEqual(['type' => 'array', 'items' => ['type' => 'string'], 'minItems' => 1, 'uniqueItems' => true]); + expect($schema['properties']['scores'])->toEqual(['type' => 'array', 'items' => ['type' => 'integer', 'minimum' => 0, 'maximum' => 100], 'minItems' => 1, 'maxItems' => 5]); + + expect($schema['required'])->toEqualCanonicalizing(['tags', 'scores']); +}); + +it('merges method-level and parameter-level #[Schema] attributes, with parameter-level taking precedence', function () { + $method = new ReflectionMethod(SchemaGeneratorFixture::class, 'methodAndParameterLevel'); + $schema = $this->schemaGenerator->generate($method); + + // Method level defines base properties + expect($schema['properties']['settingKey'])->toEqual(['type' => 'string', 'description' => 'The key of the setting.']); + + // Parameter level Schema overrides method level for newValue + expect($schema['properties']['newValue'])->toEqual(['description' => "The specific new boolean value.", 'type' => 'boolean']); + + expect($schema['required'])->toEqualCanonicalizing(['settingKey', 'newValue']); +}); + +it('combines PHP type hints, DocBlock descriptions, and parameter-level #[Schema] constraints', function () { + $method = new ReflectionMethod(SchemaGeneratorFixture::class, 'typeHintDocBlockAndParameterSchema'); + $schema = $this->schemaGenerator->generate($method); + + expect($schema['properties']['username'])->toEqual(['minLength' => 3, 'pattern' => '^[a-zA-Z0-9_]+$', 'type' => 'string', 'description' => "The user's name"]); + expect($schema['properties']['priority'])->toEqual(['minimum' => 1, 'maximum' => 10, 'type' => 'integer', 'description' => 'Task priority level']); + + expect($schema['required'])->toEqualCanonicalizing(['username', 'priority']); +}); + +it('generates correct schema for backed and unit enum parameters, inferring from type hints', function () { + $method = new ReflectionMethod(SchemaGeneratorFixture::class, 'enumParameters'); + $schema = $this->schemaGenerator->generate($method); + + expect($schema['properties']['stringEnum'])->toEqual(['type' => 'string', 'description' => 'Backed string enum', 'enum' => ['A', 'B']]); + expect($schema['properties']['intEnum'])->toEqual(['type' => 'integer', 'description' => 'Backed int enum', 'enum' => [1, 2]]); + expect($schema['properties']['unitEnum'])->toEqual(['type' => 'string', 'description' => 'Unit enum', 'enum' => ['Yes', 'No']]); + expect($schema['properties']['nullableEnum'])->toEqual(['type' => ['null', 'string'], 'enum' => ['A', 'B'], 'default' => null]); + expect($schema['properties']['enumWithDefault'])->toEqual(['type' => 'integer', 'enum' => [1, 2], 'default' => 1]); + + expect($schema['required'])->toEqualCanonicalizing(['stringEnum', 'intEnum', 'unitEnum']); +}); + +it('correctly generates schemas for various array type declarations (generic, typed, shape)', function () { + $method = new ReflectionMethod(SchemaGeneratorFixture::class, 'arrayTypeScenarios'); + $schema = $this->schemaGenerator->generate($method); + + expect($schema['properties']['genericArray'])->toEqual(['type' => 'array', 'description' => 'Generic array']); + expect($schema['properties']['stringArray'])->toEqual(['type' => 'array', 'description' => 'Array of strings', 'items' => ['type' => 'string']]); + expect($schema['properties']['intArray'])->toEqual(['type' => 'array', 'description' => 'Array of integers', 'items' => ['type' => 'integer']]); + expect($schema['properties']['mixedMap'])->toEqual(['type' => 'array', 'description' => 'Mixed array map']); + + // Object-like arrays should be converted to object type + expect($schema['properties']['objectLikeArray'])->toHaveKey('type'); + expect($schema['properties']['objectLikeArray']['type'])->toBe('object'); + expect($schema['properties']['objectLikeArray'])->toHaveKey('properties'); + expect($schema['properties']['objectLikeArray']['properties'])->toHaveKeys(['name', 'age']); + + expect($schema['required'])->toEqualCanonicalizing(['genericArray', 'stringArray', 'intArray', 'mixedMap', 'objectLikeArray', 'nestedObjectArray']); +}); + +it('handles nullable type hints and optional parameters with default values correctly', function () { + $method = new ReflectionMethod(SchemaGeneratorFixture::class, 'nullableAndOptional'); + $schema = $this->schemaGenerator->generate($method); + + expect($schema['properties']['nullableString'])->toEqual(['type' => ['null', 'string'], 'description' => 'Nullable string']); + expect($schema['properties']['nullableInt'])->toEqual(['type' => ['null', 'integer'], 'description' => 'Nullable integer', 'default' => null]); + expect($schema['properties']['optionalString'])->toEqual(['type' => 'string', 'default' => 'default']); + expect($schema['properties']['optionalBool'])->toEqual(['type' => 'boolean', 'default' => true]); + expect($schema['properties']['optionalArray'])->toEqual(['type' => 'array', 'default' => []]); + + expect($schema['required'])->toEqualCanonicalizing(['nullableString']); +}); + +it('generates schema for PHP union types, sorting types alphabetically', function () { + $method = new ReflectionMethod(SchemaGeneratorFixture::class, 'unionTypes'); + $schema = $this->schemaGenerator->generate($method); + + expect($schema['properties']['stringOrInt'])->toEqual(['type' => ['integer', 'string'], 'description' => 'String or integer']); + expect($schema['properties']['multiUnion'])->toEqual(['type' => ['null', 'boolean', 'string'], 'description' => 'Bool, string or null']); + + expect($schema['required'])->toEqualCanonicalizing(['stringOrInt', 'multiUnion']); +}); + +it('represents variadic string parameters as an array of strings', function () { + $method = new ReflectionMethod(SchemaGeneratorFixture::class, 'variadicStrings'); + $schema = $this->schemaGenerator->generate($method); + + expect($schema['properties']['items'])->toEqual(['type' => 'array', 'description' => 'Variadic strings', 'items' => ['type' => 'string']]); + expect($schema)->not->toHaveKey('required'); + // Variadic is optional +}); + +it('applies item constraints from parameter-level #[Schema] to variadic parameters', function () { + $method = new ReflectionMethod(SchemaGeneratorFixture::class, 'variadicWithConstraints'); + $schema = $this->schemaGenerator->generate($method); + + expect($schema['properties']['numbers'])->toEqual(['items' => ['type' => 'integer', 'minimum' => 0], 'type' => 'array', 'description' => 'Variadic integers']); + expect($schema)->not->toHaveKey('required'); +}); + +it('handles mixed type hints, omitting explicit type in schema and using defaults', function () { + $method = new ReflectionMethod(SchemaGeneratorFixture::class, 'mixedTypes'); + $schema = $this->schemaGenerator->generate($method); + + expect($schema['properties']['anyValue'])->toEqual(['description' => 'Any value']); + expect($schema['properties']['optionalAny'])->toEqual(['description' => 'Optional any value', 'default' => 'default']); + + expect($schema['required'])->toEqualCanonicalizing(['anyValue']); +}); + +it('generates schema for complex nested object and array structures defined in parameter-level #[Schema]', function () { + $method = new ReflectionMethod(SchemaGeneratorFixture::class, 'complexNestedSchema'); + $schema = $this->schemaGenerator->generate($method); + + expect($schema['properties']['order'])->toEqual([ + 'type' => 'object', + 'properties' => [ + 'customer' => [ + 'type' => 'object', + 'properties' => [ + 'id' => ['type' => 'string', 'pattern' => '^CUS-[0-9]{6}$'], + 'name' => ['type' => 'string', 'minLength' => 2], + 'email' => ['type' => 'string', 'format' => 'email'] + ], + 'required' => ['id', 'name'] + ], + 'items' => [ + 'type' => 'array', + 'minItems' => 1, + 'items' => [ + 'type' => 'object', + 'properties' => [ + 'product_id' => ['type' => 'string', 'pattern' => '^PRD-[0-9]{4}$'], + 'quantity' => ['type' => 'integer', 'minimum' => 1], + 'price' => ['type' => 'number', 'minimum' => 0] + ], + 'required' => ['product_id', 'quantity', 'price'] + ] + ], + 'metadata' => [ + 'type' => 'object', + 'additionalProperties' => true + ] + ], + 'required' => ['customer', 'items'] + ]); + + expect($schema['required'])->toEqual(['order']); +}); + +it('demonstrates type precedence: parameter #[Schema] overrides DocBlock, which overrides PHP type hint', function () { + $method = new ReflectionMethod(SchemaGeneratorFixture::class, 'typePrecedenceTest'); + $schema = $this->schemaGenerator->generate($method); + + // DocBlock type (integer) should override PHP type (string) + expect($schema['properties']['numericString'])->toEqual(['type' => 'integer', 'description' => 'DocBlock says integer despite string type hint']); + + // Schema constraints should be applied with PHP type + expect($schema['properties']['stringWithConstraints'])->toEqual(['format' => 'email', 'minLength' => 5, 'type' => 'string', 'description' => 'String with Schema constraints']); + + // Schema should override DocBlock array item type + expect($schema['properties']['arrayWithItems'])->toEqual(['items' => ['type' => 'integer', 'minimum' => 1, 'maximum' => 100], 'type' => 'array', 'description' => 'Array with Schema item overrides']); + + expect($schema['required'])->toEqualCanonicalizing(['numericString', 'stringWithConstraints', 'arrayWithItems']); +}); + +it('generates an empty properties object for a method with no parameters even if a method-level #[Schema] is present', function () { + $method = new ReflectionMethod(SchemaGeneratorFixture::class, 'noParamsWithSchema'); + $schema = $this->schemaGenerator->generate($method); + + expect($schema['description'])->toBe("Gets server status. Takes no arguments."); + expect($schema['properties'])->toBeInstanceOf(stdClass::class); + expect($schema)->not->toHaveKey('required'); +}); + +it('infers parameter type as "any" (omits type) if only constraints are given in #[Schema] without type hint or DocBlock type', function () { + $method = new ReflectionMethod(SchemaGeneratorFixture::class, 'parameterSchemaInferredType'); + $schema = $this->schemaGenerator->generate($method); + + expect($schema['properties']['inferredParam'])->toEqual(['description' => "Some parameter", 'minLength' => 3]); + + expect($schema['required'])->toEqual(['inferredParam']); +}); diff --git a/vendor/php-mcp/server/tests/Integration/StdioServerTransportTest.php b/vendor/php-mcp/server/tests/Integration/StdioServerTransportTest.php new file mode 100644 index 000000000..4740ef4cd --- /dev/null +++ b/vendor/php-mcp/server/tests/Integration/StdioServerTransportTest.php @@ -0,0 +1,322 @@ + '2.0', + 'id' => $requestId, + 'method' => $method, + 'params' => $params, + ]); + $process->stdin->write($request . "\n"); +} + +function sendNotificationToServer(Process $process, string $method, array $params = []): void +{ + $notification = json_encode([ + 'jsonrpc' => '2.0', + 'method' => $method, + 'params' => $params, + ]); + + $process->stdin->write($notification . "\n"); +} + +function readResponseFromServer(Process $process, string $expectedRequestId, LoopInterface $loop): PromiseInterface +{ + $deferred = new Deferred(); + $buffer = ''; + + $dataListener = function ($chunk) use (&$buffer, $deferred, $expectedRequestId, $process, &$dataListener) { + $buffer .= $chunk; + if (str_contains($buffer, "\n")) { + $lines = explode("\n", $buffer); + $buffer = array_pop($lines); + + foreach ($lines as $line) { + if (empty(trim($line))) { + continue; + } + try { + $response = json_decode(trim($line), true); + if (array_key_exists('id', $response) && $response['id'] == $expectedRequestId) { + $process->stdout->removeListener('data', $dataListener); + $deferred->resolve($response); + return; + } elseif (isset($response['method']) && str_starts_with($response['method'], 'notifications/')) { + // It's a notification, log it or handle if necessary for a specific test, but don't resolve + } + } catch (\JsonException $e) { + $process->stdout->removeListener('data', $dataListener); + $deferred->reject(new \RuntimeException("Failed to decode JSON response: " . $line, 0, $e)); + return; + } + } + } + }; + + $process->stdout->on('data', $dataListener); + + return timeout($deferred->promise(), PROCESS_TIMEOUT_SECONDS, $loop) + ->catch(function ($reason) use ($expectedRequestId) { + if ($reason instanceof \RuntimeException && str_contains($reason->getMessage(), 'Timed out after')) { + throw new \RuntimeException("Timeout waiting for response to request ID '{$expectedRequestId}'"); + } + throw $reason; + }) + ->finally(function () use ($process, $dataListener) { + $process->stdout->removeListener('data', $dataListener); + }); +} + +beforeEach(function () { + $this->loop = Loop::get(); + + if (!is_executable(STDIO_SERVER_SCRIPT_PATH)) { + chmod(STDIO_SERVER_SCRIPT_PATH, 0755); + } + + $phpPath = PHP_BINARY ?: 'php'; + $command = escapeshellarg($phpPath) . ' ' . escapeshellarg(STDIO_SERVER_SCRIPT_PATH); + $this->process = new Process($command); + $this->process->start($this->loop); + + $this->processErrorOutput = ''; + $this->process->stderr->on('data', function ($chunk) { + $this->processErrorOutput .= $chunk; + }); +}); + +afterEach(function () { + if ($this->process instanceof Process && $this->process->isRunning()) { + if ($this->process->stdin->isWritable()) { + $this->process->stdin->end(); + } + $this->process->stdout->close(); + $this->process->stdin->close(); + $this->process->stderr->close(); + $this->process->terminate(SIGTERM); + await(delay(0.05, $this->loop)); + if ($this->process->isRunning()) { + $this->process->terminate(SIGKILL); + } + } + $this->process = null; +}); + +it('starts the stdio server, initializes, calls a tool, and closes', function () { + // 1. Initialize Request + sendRequestToServer($this->process, 'init-1', 'initialize', [ + 'protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, + 'clientInfo' => ['name' => 'PestTestClient', 'version' => '1.0'], + 'capabilities' => [] + ]); + $initResponse = await(readResponseFromServer($this->process, 'init-1', $this->loop)); + + expect($initResponse['id'])->toBe('init-1'); + expect($initResponse)->not->toHaveKey('error'); + expect($initResponse['result']['protocolVersion'])->toBe(Protocol::LATEST_PROTOCOL_VERSION); + expect($initResponse['result']['serverInfo']['name'])->toBe('StdioIntegrationTestServer'); + + // 2. Initialized Notification + sendNotificationToServer($this->process, 'notifications/initialized'); + + await(delay(0.05, $this->loop)); + + // 3. Call a tool + sendRequestToServer($this->process, 'tool-call-1', 'tools/call', [ + 'name' => 'greet_stdio_tool', + 'arguments' => ['name' => 'Integration Tester'] + ]); + $toolResponse = await(readResponseFromServer($this->process, 'tool-call-1', $this->loop)); + + expect($toolResponse['id'])->toBe('tool-call-1'); + expect($toolResponse)->not->toHaveKey('error'); + expect($toolResponse['result']['content'][0]['text'])->toBe('Hello, Integration Tester!'); + expect($toolResponse['result']['isError'])->toBeFalse(); + + $this->process->stdin->end(); +})->group('integration', 'stdio_transport'); + +it('can handle invalid JSON request from client', function () { + $this->process->stdin->write("this is not json\n"); + + $response = await(readResponseFromServer($this->process, '', $this->loop)); + + expect($response['id'])->toBe(''); + expect($response['error']['code'])->toBe(-32700); + expect($response['error']['message'])->toContain('Invalid JSON'); + + $this->process->stdin->end(); +})->group('integration', 'stdio_transport'); + +it('handles request for non-existent method', function () { + sendRequestToServer($this->process, 'init-err', 'initialize', ['protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, 'clientInfo' => [], 'capabilities' => []]); + await(readResponseFromServer($this->process, 'init-err', $this->loop)); + + sendNotificationToServer($this->process, 'notifications/initialized'); + await(delay(0.05, $this->loop)); + + sendRequestToServer($this->process, 'err-meth-1', 'non/existentMethod', []); + $response = await(readResponseFromServer($this->process, 'err-meth-1', $this->loop)); + + expect($response['id'])->toBe('err-meth-1'); + expect($response['error']['code'])->toBe(-32601); + expect($response['error']['message'])->toContain("Method 'non/existentMethod' not found"); + + $this->process->stdin->end(); +})->group('integration', 'stdio_transport'); + +it('can handle batch requests correctly', function () { + // 1. Initialize + sendRequestToServer($this->process, 'init-batch', 'initialize', [ + 'protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, + 'clientInfo' => ['name' => 'BatchClient', 'version' => '1.0'], + 'capabilities' => [] + ]); + await(readResponseFromServer($this->process, 'init-batch', $this->loop)); + sendNotificationToServer($this->process, 'notifications/initialized'); + await(delay(0.05, $this->loop)); + + // 2. Send Batch Request + $batchRequests = [ + ['jsonrpc' => '2.0', 'id' => 'batch-req-1', 'method' => 'tools/call', 'params' => ['name' => 'greet_stdio_tool', 'arguments' => ['name' => 'Batch Item 1']]], + ['jsonrpc' => '2.0', 'method' => 'notifications/something'], + ['jsonrpc' => '2.0', 'id' => 'batch-req-2', 'method' => 'tools/call', 'params' => ['name' => 'greet_stdio_tool', 'arguments' => ['name' => 'Batch Item 2']]], + ['jsonrpc' => '2.0', 'id' => 'batch-req-3', 'method' => 'nonexistent/method'] + ]; + + $rawBatchRequest = json_encode($batchRequests); + $this->process->stdin->write($rawBatchRequest . "\n"); + + // 3. Read Batch Response + $batchResponsePromise = new Deferred(); + $fullBuffer = ''; + $batchDataListener = function ($chunk) use (&$fullBuffer, $batchResponsePromise, &$batchDataListener) { + $fullBuffer .= $chunk; + if (str_contains($fullBuffer, "\n")) { + $line = trim($fullBuffer); + $fullBuffer = ''; + try { + $decoded = json_decode($line, true); + if (is_array($decoded)) { // Batch response is an array + $this->process->stdout->removeListener('data', $batchDataListener); + $batchResponsePromise->resolve($decoded); + } + } catch (\JsonException $e) { + $this->process->stdout->removeListener('data', $batchDataListener); + $batchResponsePromise->reject(new \RuntimeException("Batch JSON decode failed: " . $line, 0, $e)); + } + } + }; + $this->process->stdout->on('data', $batchDataListener); + + $batchResponseArray = await(timeout($batchResponsePromise->promise(), PROCESS_TIMEOUT_SECONDS, $this->loop)); + + expect($batchResponseArray)->toBeArray()->toHaveCount(3); // greet1, greet2, error + + $response1 = array_values(array_filter($batchResponseArray, fn ($response) => $response['id'] === 'batch-req-1'))[0] ?? null; + $response2 = array_values(array_filter($batchResponseArray, fn ($response) => $response['id'] === 'batch-req-2'))[0] ?? null; + $response3 = array_values(array_filter($batchResponseArray, fn ($response) => $response['id'] === 'batch-req-3'))[0] ?? null; + + expect($response1['result']['content'][0]['text'])->toBe('Hello, Batch Item 1!'); + expect($response2['result']['content'][0]['text'])->toBe('Hello, Batch Item 2!'); + expect($response3['error']['code'])->toBe(-32601); + expect($response3['error']['message'])->toContain("Method 'nonexistent/method' not found"); + + + $this->process->stdin->end(); +})->group('integration', 'stdio_transport'); + +it('can handle tool list request', function () { + sendRequestToServer($this->process, 'init-tool-list', 'initialize', ['protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, 'clientInfo' => [], 'capabilities' => []]); + await(readResponseFromServer($this->process, 'init-tool-list', $this->loop)); + sendNotificationToServer($this->process, 'notifications/initialized'); + await(delay(0.05, $this->loop)); + + sendRequestToServer($this->process, 'tool-list-1', 'tools/list', []); + $toolListResponse = await(readResponseFromServer($this->process, 'tool-list-1', $this->loop)); + + expect($toolListResponse['id'])->toBe('tool-list-1'); + expect($toolListResponse)->not->toHaveKey('error'); + expect($toolListResponse['result']['tools'])->toBeArray()->toHaveCount(1); + expect($toolListResponse['result']['tools'][0]['name'])->toBe('greet_stdio_tool'); + + $this->process->stdin->end(); +})->group('integration', 'stdio_transport'); + +it('can read a registered resource', function () { + sendRequestToServer($this->process, 'init-res', 'initialize', ['protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, 'clientInfo' => [], 'capabilities' => []]); + await(readResponseFromServer($this->process, 'init-res', $this->loop)); + sendNotificationToServer($this->process, 'notifications/initialized'); + await(delay(0.05, $this->loop)); + + sendRequestToServer($this->process, 'res-read-1', 'resources/read', ['uri' => 'test://stdio/static']); + $resourceResponse = await(readResponseFromServer($this->process, 'res-read-1', $this->loop)); + + expect($resourceResponse['id'])->toBe('res-read-1'); + expect($resourceResponse)->not->toHaveKey('error'); + expect($resourceResponse['result']['contents'])->toBeArray()->toHaveCount(1); + expect($resourceResponse['result']['contents'][0]['uri'])->toBe('test://stdio/static'); + expect($resourceResponse['result']['contents'][0]['text'])->toBe(ResourceHandlerFixture::$staticTextContent); + expect($resourceResponse['result']['contents'][0]['mimeType'])->toBe('text/plain'); + + $this->process->stdin->end(); +})->group('integration', 'stdio_transport'); + +it('can get a registered prompt', function () { + sendRequestToServer($this->process, 'init-prompt', 'initialize', ['protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, 'clientInfo' => [], 'capabilities' => []]); + await(readResponseFromServer($this->process, 'init-prompt', $this->loop)); + sendNotificationToServer($this->process, 'notifications/initialized'); + await(delay(0.05, $this->loop)); + + sendRequestToServer($this->process, 'prompt-get-1', 'prompts/get', [ + 'name' => 'simple_stdio_prompt', + 'arguments' => ['name' => 'StdioPromptUser'] + ]); + $promptResponse = await(readResponseFromServer($this->process, 'prompt-get-1', $this->loop)); + + expect($promptResponse['id'])->toBe('prompt-get-1'); + expect($promptResponse)->not->toHaveKey('error'); + expect($promptResponse['result']['messages'])->toBeArray()->toHaveCount(1); + expect($promptResponse['result']['messages'][0]['role'])->toBe('user'); + expect($promptResponse['result']['messages'][0]['content']['text'])->toBe('Craft a friendly greeting for StdioPromptUser.'); + + $this->process->stdin->end(); +})->group('integration', 'stdio_transport'); + +it('handles client not sending initialized notification before other requests', function () { + sendRequestToServer($this->process, 'init-no-ack', 'initialize', [ + 'protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, + 'clientInfo' => ['name' => 'ForgetfulClient', 'version' => '1.0'], + 'capabilities' => [] + ]); + await(readResponseFromServer($this->process, 'init-no-ack', $this->loop)); + // Client "forgets" to send notifications/initialized + + + sendRequestToServer($this->process, 'tool-call-no-ack', 'tools/call', [ + 'name' => 'greet_stdio_tool', + 'arguments' => ['name' => 'NoAckUser'] + ]); + $toolResponse = await(readResponseFromServer($this->process, 'tool-call-no-ack', $this->loop)); + + expect($toolResponse['id'])->toBe('tool-call-no-ack'); + expect($toolResponse['error']['code'])->toBe(-32600); + expect($toolResponse['error']['message'])->toContain('Client session not initialized'); + + $this->process->stdin->end(); +})->group('integration', 'stdio_transport'); diff --git a/vendor/php-mcp/server/tests/Integration/StreamableHttpServerTransportTest.php b/vendor/php-mcp/server/tests/Integration/StreamableHttpServerTransportTest.php new file mode 100644 index 000000000..fd0f0c3b2 --- /dev/null +++ b/vendor/php-mcp/server/tests/Integration/StreamableHttpServerTransportTest.php @@ -0,0 +1,811 @@ +markTestSkipped("Server script not found: " . STREAMABLE_HTTP_SCRIPT_PATH); + } + if (!is_executable(STREAMABLE_HTTP_SCRIPT_PATH)) { + chmod(STREAMABLE_HTTP_SCRIPT_PATH, 0755); + } + + $phpPath = PHP_BINARY ?: 'php'; + $commandPhpPath = str_contains($phpPath, ' ') ? '"' . $phpPath . '"' : $phpPath; + $commandScriptPath = escapeshellarg(STREAMABLE_HTTP_SCRIPT_PATH); + $this->port = findFreePort(); + + $jsonModeCommandArgs = [ + escapeshellarg(STREAMABLE_HTTP_HOST), + escapeshellarg((string)$this->port), + escapeshellarg(STREAMABLE_MCP_PATH), + escapeshellarg('true'), // enableJsonResponse = true + ]; + $this->jsonModeCommand = $commandPhpPath . ' ' . $commandScriptPath . ' ' . implode(' ', $jsonModeCommandArgs); + + $streamModeCommandArgs = [ + escapeshellarg(STREAMABLE_HTTP_HOST), + escapeshellarg((string)$this->port), + escapeshellarg(STREAMABLE_MCP_PATH), + escapeshellarg('false'), // enableJsonResponse = false + ]; + $this->streamModeCommand = $commandPhpPath . ' ' . $commandScriptPath . ' ' . implode(' ', $streamModeCommandArgs); + + $statelessModeCommandArgs = [ + escapeshellarg(STREAMABLE_HTTP_HOST), + escapeshellarg((string)$this->port), + escapeshellarg(STREAMABLE_MCP_PATH), + escapeshellarg('true'), // enableJsonResponse = true + escapeshellarg('false'), // useEventStore = false + escapeshellarg('true'), // stateless = true + ]; + $this->statelessModeCommand = $commandPhpPath . ' ' . $commandScriptPath . ' ' . implode(' ', $statelessModeCommandArgs); + + $this->process = null; +}); + +afterEach(function () { + if ($this->process instanceof Process && $this->process->isRunning()) { + if ($this->process->stdout instanceof ReadableStreamInterface) { + $this->process->stdout->close(); + } + if ($this->process->stderr instanceof ReadableStreamInterface) { + $this->process->stderr->close(); + } + + $this->process->terminate(SIGTERM); + try { + await(delay(0.02)); + } catch (\Throwable $e) { + } + if ($this->process->isRunning()) { + $this->process->terminate(SIGKILL); + } + } + $this->process = null; +}); + +describe('JSON MODE', function () { + beforeEach(function () { + $this->process = new Process($this->jsonModeCommand, getcwd() ?: null, null, []); + $this->process->start(); + + $this->jsonClient = new MockJsonHttpClient(STREAMABLE_HTTP_HOST, $this->port, STREAMABLE_MCP_PATH); + + await(delay(0.2)); + }); + + it('server starts, initializes via POST JSON, calls a tool, and closes', function () { + // 1. Initialize + $initResult = await($this->jsonClient->sendRequest('initialize', [ + 'protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, + 'clientInfo' => ['name' => 'JsonModeClient', 'version' => '1.0'], + 'capabilities' => [] + ], 'init-json-1')); + + expect($initResult['statusCode'])->toBe(200); + expect($initResult['body']['id'])->toBe('init-json-1'); + expect($initResult['body'])->not->toHaveKey('error'); + expect($initResult['body']['result']['protocolVersion'])->toBe(Protocol::LATEST_PROTOCOL_VERSION); + expect($initResult['body']['result']['serverInfo']['name'])->toBe('StreamableHttpIntegrationServer'); + expect($this->jsonClient->sessionId)->toBeString()->not->toBeEmpty(); + + // 2. Initialized notification + $notifResult = await($this->jsonClient->sendNotification('notifications/initialized')); + expect($notifResult['statusCode'])->toBe(202); + + // 3. Call a tool + $toolResult = await($this->jsonClient->sendRequest('tools/call', [ + 'name' => 'greet_streamable_tool', + 'arguments' => ['name' => 'JSON Mode User'] + ], 'tool-json-1')); + + expect($toolResult['statusCode'])->toBe(200); + expect($toolResult['body']['id'])->toBe('tool-json-1'); + expect($toolResult['body'])->not->toHaveKey('error'); + expect($toolResult['body']['result']['content'][0]['text'])->toBe('Hello, JSON Mode User!'); + + // Server process is terminated in afterEach + })->group('integration', 'streamable_http_json'); + + + it('return HTTP 400 error response for invalid JSON in POST request', function () { + $malformedJson = '{"jsonrpc":"2.0", "id": "bad-json-post-1", "method": "tools/list", "params": {"broken"}'; + + $promise = $this->jsonClient->browser->post( + $this->jsonClient->baseUrl, + ['Content-Type' => 'application/json', 'Accept' => 'application/json'], + $malformedJson + ); + + try { + await(timeout($promise, STREAMABLE_HTTP_PROCESS_TIMEOUT - 2)); + } catch (ResponseException $e) { + expect($e->getResponse()->getStatusCode())->toBe(400); + $bodyContent = (string) $e->getResponse()->getBody(); + $decodedBody = json_decode($bodyContent, true); + + expect($decodedBody['jsonrpc'])->toBe('2.0'); + expect($decodedBody['id'])->toBe(''); + expect($decodedBody['error']['code'])->toBe(-32700); + expect($decodedBody['error']['message'])->toContain('Invalid JSON'); + } + })->group('integration', 'streamable_http_json'); + + it('returns JSON-RPC error result for request for non-existent method', function () { + // 1. Initialize + await($this->jsonClient->sendRequest('initialize', ['protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, 'clientInfo' => ['name' => 'JsonModeClient', 'version' => '1.0'], 'capabilities' => []], 'init-json-err')); + await($this->jsonClient->sendNotification('notifications/initialized')); + + // 2. Request non-existent method + $errorResult = await($this->jsonClient->sendRequest('non/existentToolViaJson', [], 'err-meth-json-1')); + + expect($errorResult['statusCode'])->toBe(200); + expect($errorResult['body']['id'])->toBe('err-meth-json-1'); + expect($errorResult['body']['error']['code'])->toBe(-32601); + expect($errorResult['body']['error']['message'])->toContain("Method 'non/existentToolViaJson' not found"); + })->group('integration', 'streamable_http_json'); + + it('can handle batch requests correctly', function () { + // 1. Initialize + await($this->jsonClient->sendRequest('initialize', [ + 'protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, + 'clientInfo' => ['name' => 'JsonModeClient', 'version' => '1.0'], + 'capabilities' => [] + ], 'init-json-batch')); + expect($this->jsonClient->sessionId)->toBeString()->not->toBeEmpty(); + await($this->jsonClient->sendNotification('notifications/initialized')); + + // 2. Send Batch Request + $batchRequests = [ + ['jsonrpc' => '2.0', 'id' => 'batch-req-1', 'method' => 'tools/call', 'params' => ['name' => 'greet_streamable_tool', 'arguments' => ['name' => 'Batch Item 1']]], + ['jsonrpc' => '2.0', 'method' => 'notifications/something'], + ['jsonrpc' => '2.0', 'id' => 'batch-req-2', 'method' => 'tools/call', 'params' => ['name' => 'sum_streamable_tool', 'arguments' => ['a' => 10, 'b' => 20]]], + ['jsonrpc' => '2.0', 'id' => 'batch-req-3', 'method' => 'nonexistent/method'] + ]; + + $batchResponse = await($this->jsonClient->sendBatchRequest($batchRequests)); + + + + $findResponseById = function (array $batch, $id) { + foreach ($batch as $item) { + if (isset($item['id']) && $item['id'] === $id) { + return $item; + } + } + return null; + }; + + expect($batchResponse['statusCode'])->toBe(200); + expect($batchResponse['body'])->toBeArray()->toHaveCount(3); + + $response1 = $findResponseById($batchResponse['body'], 'batch-req-1'); + $response2 = $findResponseById($batchResponse['body'], 'batch-req-2'); + $response3 = $findResponseById($batchResponse['body'], 'batch-req-3'); + + expect($response1['result']['content'][0]['text'])->toBe('Hello, Batch Item 1!'); + expect($response2['result']['content'][0]['text'])->toBe('30'); + expect($response3['error']['code'])->toBe(-32601); + expect($response3['error']['message'])->toContain("Method 'nonexistent/method' not found"); + })->group('integration', 'streamable_http_json'); + + it('can handle tool list request', function () { + await($this->jsonClient->sendRequest('initialize', [ + 'protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, + 'clientInfo' => ['name' => 'JsonModeClient', 'version' => '1.0'], + 'capabilities' => [] + ], 'init-json-tools')); + await($this->jsonClient->sendNotification('notifications/initialized')); + + $toolListResult = await($this->jsonClient->sendRequest('tools/list', [], 'tool-list-json-1')); + + expect($toolListResult['statusCode'])->toBe(200); + expect($toolListResult['body']['id'])->toBe('tool-list-json-1'); + expect($toolListResult['body']['result']['tools'])->toBeArray(); + expect(count($toolListResult['body']['result']['tools']))->toBe(2); + expect($toolListResult['body']['result']['tools'][0]['name'])->toBe('greet_streamable_tool'); + expect($toolListResult['body']['result']['tools'][1]['name'])->toBe('sum_streamable_tool'); + })->group('integration', 'streamable_http_json'); + + it('can read a registered resource', function () { + await($this->jsonClient->sendRequest('initialize', [ + 'protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, + 'clientInfo' => ['name' => 'JsonModeClient', 'version' => '1.0'], + 'capabilities' => [] + ], 'init-json-res')); + await($this->jsonClient->sendNotification('notifications/initialized')); + + $resourceResult = await($this->jsonClient->sendRequest('resources/read', ['uri' => 'test://streamable/static'], 'res-read-json-1')); + + expect($resourceResult['statusCode'])->toBe(200); + expect($resourceResult['body']['id'])->toBe('res-read-json-1'); + $contents = $resourceResult['body']['result']['contents']; + expect($contents[0]['uri'])->toBe('test://streamable/static'); + expect($contents[0]['text'])->toBe(\PhpMcp\Server\Tests\Fixtures\General\ResourceHandlerFixture::$staticTextContent); + })->group('integration', 'streamable_http_json'); + + it('can get a registered prompt', function () { + await($this->jsonClient->sendRequest('initialize', ['protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, 'clientInfo' => ['name' => 'JsonModeClient', 'version' => '1.0'], 'capabilities' => []], 'init-json-prompt')); + await($this->jsonClient->sendNotification('notifications/initialized')); + + $promptResult = await($this->jsonClient->sendRequest('prompts/get', [ + 'name' => 'simple_streamable_prompt', + 'arguments' => ['name' => 'JsonPromptUser', 'style' => 'terse'] + ], 'prompt-get-json-1')); + + expect($promptResult['statusCode'])->toBe(200); + expect($promptResult['body']['id'])->toBe('prompt-get-json-1'); + $messages = $promptResult['body']['result']['messages']; + expect($messages[0]['content']['text'])->toBe('Craft a terse greeting for JsonPromptUser.'); + })->group('integration', 'streamable_http_json'); + + it('rejects subsequent requests if client does not send initialized notification', function () { + // 1. Initialize ONLY + await($this->jsonClient->sendRequest('initialize', ['protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, 'clientInfo' => ['name' => 'JsonModeClient', 'version' => '1.0'], 'capabilities' => []], 'init-json-noack')); + // Client "forgets" to send notifications/initialized back + + // 2. Attempt to Call a tool + $toolResult = await($this->jsonClient->sendRequest('tools/call', [ + 'name' => 'greet_streamable_tool', + 'arguments' => ['name' => 'NoAckJsonUser'] + ], 'tool-json-noack')); + + expect($toolResult['statusCode'])->toBe(200); // HTTP is fine + expect($toolResult['body']['id'])->toBe('tool-json-noack'); + expect($toolResult['body']['error']['code'])->toBe(-32600); // Invalid Request + expect($toolResult['body']['error']['message'])->toContain('Client session not initialized'); + })->group('integration', 'streamable_http_json'); + + it('returns HTTP 400 error for non-initialize requests without Mcp-Session-Id', function () { + await($this->jsonClient->sendRequest('initialize', ['protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, 'clientInfo' => ['name' => 'JsonModeClient', 'version' => '1.0'], 'capabilities' => []], 'init-sess-test')); + $this->jsonClient->sessionId = null; + + try { + await($this->jsonClient->sendRequest('tools/list', [], 'tools-list-no-session')); + } catch (ResponseException $e) { + expect($e->getResponse()->getStatusCode())->toBe(400); + $bodyContent = (string) $e->getResponse()->getBody(); + $decodedBody = json_decode($bodyContent, true); + + expect($decodedBody['jsonrpc'])->toBe('2.0'); + expect($decodedBody['id'])->toBe('tools-list-no-session'); + expect($decodedBody['error']['code'])->toBe(-32600); + expect($decodedBody['error']['message'])->toContain('Mcp-Session-Id header required'); + } + })->group('integration', 'streamable_http_json'); +}); + +describe('STREAM MODE', function () { + beforeEach(function () { + $this->process = new Process($this->streamModeCommand, getcwd() ?: null, null, []); + $this->process->start(); + $this->streamClient = new MockStreamHttpClient(STREAMABLE_HTTP_HOST, $this->port, STREAMABLE_MCP_PATH); + await(delay(0.2)); + }); + afterEach(function () { + if ($this->streamClient ?? null) { + $this->streamClient->closeMainSseStream(); + } + }); + + it('server starts, initializes via POST JSON, calls a tool, and closes', function () { + // 1. Initialize Request + $initResponse = await($this->streamClient->sendInitializeRequest([ + 'protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, + 'clientInfo' => ['name' => 'StreamModeClient', 'version' => '1.0'], + 'capabilities' => [] + ], 'init-stream-1')); + + expect($this->streamClient->sessionId)->toBeString()->not->toBeEmpty(); + expect($initResponse['id'])->toBe('init-stream-1'); + expect($initResponse)->not->toHaveKey('error'); + expect($initResponse['result']['protocolVersion'])->toBe(Protocol::LATEST_PROTOCOL_VERSION); + expect($initResponse['result']['serverInfo']['name'])->toBe('StreamableHttpIntegrationServer'); + + // 2. Send Initialized Notification + $notifResult = await($this->streamClient->sendHttpNotification('notifications/initialized')); + expect($notifResult['statusCode'])->toBe(202); + + // 3. Call a tool + $toolResponse = await($this->streamClient->sendRequest('tools/call', [ + 'name' => 'greet_streamable_tool', + 'arguments' => ['name' => 'Stream Mode User'] + ], 'tool-stream-1')); + + expect($toolResponse['id'])->toBe('tool-stream-1'); + expect($toolResponse)->not->toHaveKey('error'); + expect($toolResponse['result']['content'][0]['text'])->toBe('Hello, Stream Mode User!'); + })->group('integration', 'streamable_http_stream'); + + it('return HTTP 400 error response for invalid JSON in POST request', function () { + $malformedJson = '{"jsonrpc":"2.0", "id": "bad-json-stream-1", "method": "tools/list", "params": {"broken"}'; + + $postPromise = $this->streamClient->browser->post( + $this->streamClient->baseMcpUrl, + ['Content-Type' => 'application/json', 'Accept' => 'text/event-stream'], + $malformedJson + ); + + try { + await(timeout($postPromise, STREAMABLE_HTTP_PROCESS_TIMEOUT - 2)); + } catch (ResponseException $e) { + $httpResponse = $e->getResponse(); + $bodyContent = (string) $httpResponse->getBody(); + $decodedBody = json_decode($bodyContent, true); + + expect($httpResponse->getStatusCode())->toBe(400); + expect($decodedBody['jsonrpc'])->toBe('2.0'); + expect($decodedBody['id'])->toBe(''); + expect($decodedBody['error']['code'])->toBe(-32700); + expect($decodedBody['error']['message'])->toContain('Invalid JSON'); + } + })->group('integration', 'streamable_http_stream'); + + it('returns JSON-RPC error result for request for non-existent method', function () { + // 1. Initialize + await($this->streamClient->sendInitializeRequest([ + 'protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, + 'clientInfo' => ['name' => 'StreamModeClient', 'version' => '1.0'], + 'capabilities' => [] + ], 'init-stream-err')); + await($this->streamClient->sendHttpNotification('notifications/initialized')); + + // 2. Send Request + $errorResponse = await($this->streamClient->sendRequest('non/existentToolViaStream', [], 'err-meth-stream-1')); + + expect($errorResponse['id'])->toBe('err-meth-stream-1'); + expect($errorResponse['error']['code'])->toBe(-32601); + expect($errorResponse['error']['message'])->toContain("Method 'non/existentToolViaStream' not found"); + })->group('integration', 'streamable_http_stream'); + + it('can handle batch requests correctly', function () { + await($this->streamClient->sendInitializeRequest([ + 'protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, + 'clientInfo' => ['name' => 'StreamModeBatchClient', 'version' => '1.0'], + 'capabilities' => [] + ], 'init-stream-batch')); + expect($this->streamClient->sessionId)->toBeString()->not->toBeEmpty(); + await($this->streamClient->sendHttpNotification('notifications/initialized')); + + $batchRequests = [ + ['jsonrpc' => '2.0', 'id' => 'batch-req-1', 'method' => 'tools/call', 'params' => ['name' => 'greet_streamable_tool', 'arguments' => ['name' => 'Batch Item 1']]], + ['jsonrpc' => '2.0', 'method' => 'notifications/something'], + ['jsonrpc' => '2.0', 'id' => 'batch-req-2', 'method' => 'tools/call', 'params' => ['name' => 'sum_streamable_tool', 'arguments' => ['a' => 10, 'b' => 20]]], + ['jsonrpc' => '2.0', 'id' => 'batch-req-3', 'method' => 'nonexistent/method'] + ]; + + $batchResponseArray = await($this->streamClient->sendBatchRequest($batchRequests)); + + expect($batchResponseArray)->toBeArray()->toHaveCount(3); + + $findResponseById = function (array $batch, $id) { + foreach ($batch as $item) { + if (isset($item['id']) && $item['id'] === $id) { + return $item; + } + } + return null; + }; + + $response1 = $findResponseById($batchResponseArray, 'batch-req-1'); + $response2 = $findResponseById($batchResponseArray, 'batch-req-2'); + $response3 = $findResponseById($batchResponseArray, 'batch-req-3'); + + expect($response1['result']['content'][0]['text'])->toBe('Hello, Batch Item 1!'); + expect($response2['result']['content'][0]['text'])->toBe('30'); + expect($response3['error']['code'])->toBe(-32601); + expect($response3['error']['message'])->toContain("Method 'nonexistent/method' not found"); + })->group('integration', 'streamable_http_stream'); + + it('can handle tool list request', function () { + await($this->streamClient->sendInitializeRequest(['protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, 'clientInfo' => []], 'init-stream-tools')); + await($this->streamClient->sendHttpNotification('notifications/initialized')); + + $toolListResponse = await($this->streamClient->sendRequest('tools/list', [], 'tool-list-stream-1')); + + expect($toolListResponse['id'])->toBe('tool-list-stream-1'); + expect($toolListResponse)->not->toHaveKey('error'); + expect($toolListResponse['result']['tools'])->toBeArray(); + expect(count($toolListResponse['result']['tools']))->toBe(2); + expect($toolListResponse['result']['tools'][0]['name'])->toBe('greet_streamable_tool'); + expect($toolListResponse['result']['tools'][1]['name'])->toBe('sum_streamable_tool'); + })->group('integration', 'streamable_http_stream'); + + it('can read a registered resource', function () { + await($this->streamClient->sendInitializeRequest(['protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, 'clientInfo' => []], 'init-stream-res')); + await($this->streamClient->sendHttpNotification('notifications/initialized')); + + $resourceResponse = await($this->streamClient->sendRequest('resources/read', ['uri' => 'test://streamable/static'], 'res-read-stream-1')); + + expect($resourceResponse['id'])->toBe('res-read-stream-1'); + $contents = $resourceResponse['result']['contents']; + expect($contents[0]['uri'])->toBe('test://streamable/static'); + expect($contents[0]['text'])->toBe(\PhpMcp\Server\Tests\Fixtures\General\ResourceHandlerFixture::$staticTextContent); + })->group('integration', 'streamable_http_stream'); + + it('can get a registered prompt', function () { + await($this->streamClient->sendInitializeRequest(['protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, 'clientInfo' => []], 'init-stream-prompt')); + await($this->streamClient->sendHttpNotification('notifications/initialized')); + + $promptResponse = await($this->streamClient->sendRequest('prompts/get', [ + 'name' => 'simple_streamable_prompt', + 'arguments' => ['name' => 'StreamPromptUser', 'style' => 'formal'] + ], 'prompt-get-stream-1')); + + expect($promptResponse['id'])->toBe('prompt-get-stream-1'); + $messages = $promptResponse['result']['messages']; + expect($messages[0]['content']['text'])->toBe('Craft a formal greeting for StreamPromptUser.'); + })->group('integration', 'streamable_http_stream'); + + it('rejects subsequent requests if client does not send initialized notification', function () { + await($this->streamClient->sendInitializeRequest([ + 'protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, + 'clientInfo' => ['name' => 'StreamModeClient', 'version' => '1.0'], + 'capabilities' => [] + ], 'init-stream-noack')); + + $toolResponse = await($this->streamClient->sendRequest('tools/call', [ + 'name' => 'greet_streamable_tool', + 'arguments' => ['name' => 'NoAckStreamUser'] + ], 'tool-stream-noack')); + + expect($toolResponse['id'])->toBe('tool-stream-noack'); + expect($toolResponse['error']['code'])->toBe(-32600); + expect($toolResponse['error']['message'])->toContain('Client session not initialized'); + })->group('integration', 'streamable_http_stream'); + + it('returns HTTP 400 error for non-initialize requests without Mcp-Session-Id', function () { + await($this->streamClient->sendInitializeRequest([ + 'protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, + 'clientInfo' => ['name' => 'StreamModeClient', 'version' => '1.0'], + 'capabilities' => [] + ], 'init-stream-sess-test')); + $validSessionId = $this->streamClient->sessionId; + $this->streamClient->sessionId = null; + + try { + await($this->streamClient->sendRequest('tools/list', [], 'tools-list-no-session-stream')); + $this->fail("Expected request to tools/list to fail with 400, but it succeeded."); + } catch (ResponseException $e) { + expect($e->getResponse()->getStatusCode())->toBe(400); + // Body can't be a json since the header accepts only text/event-stream + } + + $this->streamClient->sessionId = $validSessionId; + })->group('integration', 'streamable_http_stream'); +}); + +/** + * STATELESS MODE TESTS + * + * Tests for the stateless mode of StreamableHttpServerTransport, which: + * - Generates session IDs internally but doesn't expose them to clients + * - Doesn't require session IDs in requests after initialization + * - Doesn't include session IDs in response headers + * - Disables GET requests (SSE streaming) + * - Makes DELETE requests meaningless (but returns 204) + * - Treats each request as independent (no persistent session state) + * + * This mode is designed to work with clients like OpenAI's MCP implementation + * that have issues with session management in "never require approval" mode. + */ +describe('STATELESS MODE', function () { + beforeEach(function () { + $this->process = new Process($this->statelessModeCommand, getcwd() ?: null, null, []); + $this->process->start(); + $this->statelessClient = new MockJsonHttpClient(STREAMABLE_HTTP_HOST, $this->port, STREAMABLE_MCP_PATH); + await(delay(0.2)); + }); + + it('allows tool calls without having to send initialized notification', function () { + // 1. Initialize Request + $initResult = await($this->statelessClient->sendRequest('initialize', [ + 'protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, + 'clientInfo' => ['name' => 'StatelessModeClient', 'version' => '1.0'], + 'capabilities' => [] + ], 'init-stateless-1')); + + expect($initResult['statusCode'])->toBe(200); + expect($initResult['body']['id'])->toBe('init-stateless-1'); + expect($initResult['body'])->not->toHaveKey('error'); + expect($initResult['body']['result']['protocolVersion'])->toBe(Protocol::LATEST_PROTOCOL_VERSION); + expect($initResult['body']['result']['serverInfo']['name'])->toBe('StreamableHttpIntegrationServer'); + expect($this->statelessClient->sessionId)->toBeString()->toBeEmpty(); + + // 2. Call a tool + $toolResult = await($this->statelessClient->sendRequest('tools/call', [ + 'name' => 'greet_streamable_tool', + 'arguments' => ['name' => 'Stateless Mode User'] + ], 'tool-stateless-1')); + + expect($toolResult['statusCode'])->toBe(200); + expect($toolResult['body']['id'])->toBe('tool-stateless-1'); + expect($toolResult['body'])->not->toHaveKey('error'); + expect($toolResult['body']['result']['content'][0]['text'])->toBe('Hello, Stateless Mode User!'); + })->group('integration', 'streamable_http_stateless'); + + it('return HTTP 400 error response for invalid JSON in POST request', function () { + $malformedJson = '{"jsonrpc":"2.0", "id": "bad-json-stateless-1", "method": "tools/list", "params": {"broken"}'; + + $postPromise = $this->statelessClient->browser->post( + $this->statelessClient->baseUrl, + ['Content-Type' => 'application/json', 'Accept' => 'application/json'], + $malformedJson + ); + + try { + await(timeout($postPromise, STREAMABLE_HTTP_PROCESS_TIMEOUT - 2)); + } catch (ResponseException $e) { + $httpResponse = $e->getResponse(); + $bodyContent = (string) $httpResponse->getBody(); + $decodedBody = json_decode($bodyContent, true); + + expect($httpResponse->getStatusCode())->toBe(400); + expect($decodedBody['jsonrpc'])->toBe('2.0'); + expect($decodedBody['id'])->toBe(''); + expect($decodedBody['error']['code'])->toBe(-32700); + expect($decodedBody['error']['message'])->toContain('Invalid JSON'); + } + })->group('integration', 'streamable_http_stateless'); + + it('returns JSON-RPC error result for request for non-existent method', function () { + $errorResult = await($this->statelessClient->sendRequest('non/existentToolViaStateless', [], 'err-meth-stateless-1')); + + expect($errorResult['statusCode'])->toBe(200); + expect($errorResult['body']['id'])->toBe('err-meth-stateless-1'); + expect($errorResult['body']['error']['code'])->toBe(-32601); + expect($errorResult['body']['error']['message'])->toContain("Method 'non/existentToolViaStateless' not found"); + })->group('integration', 'streamable_http_stateless'); + + it('can handle batch requests correctly', function () { + $batchRequests = [ + ['jsonrpc' => '2.0', 'id' => 'batch-req-1', 'method' => 'tools/call', 'params' => ['name' => 'greet_streamable_tool', 'arguments' => ['name' => 'Batch Item 1']]], + ['jsonrpc' => '2.0', 'method' => 'notifications/something'], + ['jsonrpc' => '2.0', 'id' => 'batch-req-2', 'method' => 'tools/call', 'params' => ['name' => 'sum_streamable_tool', 'arguments' => ['a' => 10, 'b' => 20]]], + ['jsonrpc' => '2.0', 'id' => 'batch-req-3', 'method' => 'nonexistent/method'] + ]; + + $batchResponse = await($this->statelessClient->sendBatchRequest($batchRequests)); + + $findResponseById = function (array $batch, $id) { + foreach ($batch as $item) { + if (isset($item['id']) && $item['id'] === $id) { + return $item; + } + } + return null; + }; + + expect($batchResponse['statusCode'])->toBe(200); + expect($batchResponse['body'])->toBeArray()->toHaveCount(3); + + $response1 = $findResponseById($batchResponse['body'], 'batch-req-1'); + $response2 = $findResponseById($batchResponse['body'], 'batch-req-2'); + $response3 = $findResponseById($batchResponse['body'], 'batch-req-3'); + + expect($response1['result']['content'][0]['text'])->toBe('Hello, Batch Item 1!'); + expect($response2['result']['content'][0]['text'])->toBe('30'); + expect($response3['error']['code'])->toBe(-32601); + expect($response3['error']['message'])->toContain("Method 'nonexistent/method' not found"); + })->group('integration', 'streamable_http_stateless'); + + it('can handle tool list request', function () { + $toolListResult = await($this->statelessClient->sendRequest('tools/list', [], 'tool-list-stateless-1')); + + expect($toolListResult['statusCode'])->toBe(200); + expect($toolListResult['body']['id'])->toBe('tool-list-stateless-1'); + expect($toolListResult['body'])->not->toHaveKey('error'); + expect($toolListResult['body']['result']['tools'])->toBeArray(); + expect(count($toolListResult['body']['result']['tools']))->toBe(2); + expect($toolListResult['body']['result']['tools'][0]['name'])->toBe('greet_streamable_tool'); + expect($toolListResult['body']['result']['tools'][1]['name'])->toBe('sum_streamable_tool'); + })->group('integration', 'streamable_http_stateless'); + + it('can read a registered resource', function () { + $resourceResult = await($this->statelessClient->sendRequest('resources/read', ['uri' => 'test://streamable/static'], 'res-read-stateless-1')); + + expect($resourceResult['statusCode'])->toBe(200); + expect($resourceResult['body']['id'])->toBe('res-read-stateless-1'); + $contents = $resourceResult['body']['result']['contents']; + expect($contents[0]['uri'])->toBe('test://streamable/static'); + expect($contents[0]['text'])->toBe(\PhpMcp\Server\Tests\Fixtures\General\ResourceHandlerFixture::$staticTextContent); + })->group('integration', 'streamable_http_stateless'); + + it('can get a registered prompt', function () { + $promptResult = await($this->statelessClient->sendRequest('prompts/get', [ + 'name' => 'simple_streamable_prompt', + 'arguments' => ['name' => 'StatelessPromptUser', 'style' => 'formal'] + ], 'prompt-get-stateless-1')); + + expect($promptResult['statusCode'])->toBe(200); + expect($promptResult['body']['id'])->toBe('prompt-get-stateless-1'); + $messages = $promptResult['body']['result']['messages']; + expect($messages[0]['content']['text'])->toBe('Craft a formal greeting for StatelessPromptUser.'); + })->group('integration', 'streamable_http_stateless'); + + it('does not return session ID in response headers in stateless mode', function () { + $promise = $this->statelessClient->browser->post( + $this->statelessClient->baseUrl, + ['Content-Type' => 'application/json', 'Accept' => 'application/json'], + json_encode([ + 'jsonrpc' => '2.0', + 'id' => 'init-header-test', + 'method' => 'initialize', + 'params' => [ + 'protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, + 'clientInfo' => ['name' => 'StatelessHeaderTest', 'version' => '1.0'], + 'capabilities' => [] + ] + ]) + ); + + $response = await(timeout($promise, STREAMABLE_HTTP_PROCESS_TIMEOUT - 2)); + + expect($response->getStatusCode())->toBe(200); + expect($response->hasHeader('Mcp-Session-Id'))->toBeFalse(); + + $body = json_decode((string) $response->getBody(), true); + expect($body['id'])->toBe('init-header-test'); + expect($body)->not->toHaveKey('error'); + })->group('integration', 'streamable_http_stateless'); + + it('returns HTTP 405 for GET requests (SSE disabled) in stateless mode', function () { + try { + $getPromise = $this->statelessClient->browser->get( + $this->statelessClient->baseUrl, + ['Accept' => 'text/event-stream'] + ); + await(timeout($getPromise, STREAMABLE_HTTP_PROCESS_TIMEOUT - 2)); + $this->fail("Expected GET request to fail with 405, but it succeeded."); + } catch (ResponseException $e) { + expect($e->getResponse()->getStatusCode())->toBe(405); + $bodyContent = (string) $e->getResponse()->getBody(); + $decodedBody = json_decode($bodyContent, true); + expect($decodedBody['error']['message'])->toContain('GET requests (SSE streaming) are not supported in stateless mode'); + } + })->group('integration', 'streamable_http_stateless'); + + it('returns 204 for DELETE requests in stateless mode (but they are meaningless)', function () { + $deletePromise = $this->statelessClient->browser->delete($this->statelessClient->baseUrl); + $response = await(timeout($deletePromise, STREAMABLE_HTTP_PROCESS_TIMEOUT - 2)); + + expect($response->getStatusCode())->toBe(204); + expect((string) $response->getBody())->toBeEmpty(); + })->group('integration', 'streamable_http_stateless'); + + it('handles multiple independent tool calls in stateless mode', function () { + $toolResult1 = await($this->statelessClient->sendRequest('tools/call', [ + 'name' => 'greet_streamable_tool', + 'arguments' => ['name' => 'User 1'] + ], 'tool-multi-1')); + + $toolResult2 = await($this->statelessClient->sendRequest('tools/call', [ + 'name' => 'sum_streamable_tool', + 'arguments' => ['a' => 5, 'b' => 10] + ], 'tool-multi-2')); + + $toolResult3 = await($this->statelessClient->sendRequest('tools/call', [ + 'name' => 'greet_streamable_tool', + 'arguments' => ['name' => 'User 3'] + ], 'tool-multi-3')); + + expect($toolResult1['statusCode'])->toBe(200); + expect($toolResult1['body']['id'])->toBe('tool-multi-1'); + expect($toolResult1['body']['result']['content'][0]['text'])->toBe('Hello, User 1!'); + + expect($toolResult2['statusCode'])->toBe(200); + expect($toolResult2['body']['id'])->toBe('tool-multi-2'); + expect($toolResult2['body']['result']['content'][0]['text'])->toBe('15'); + + expect($toolResult3['statusCode'])->toBe(200); + expect($toolResult3['body']['id'])->toBe('tool-multi-3'); + expect($toolResult3['body']['result']['content'][0]['text'])->toBe('Hello, User 3!'); + })->group('integration', 'streamable_http_stateless'); +}); + +it('responds to OPTIONS request with CORS headers', function () { + $this->process = new Process($this->jsonModeCommand, getcwd() ?: null, null, []); + $this->process->start(); + $this->jsonClient = new MockJsonHttpClient(STREAMABLE_HTTP_HOST, $this->port, STREAMABLE_MCP_PATH); + await(delay(0.1)); + + $browser = new Browser(); + $optionsUrl = $this->jsonClient->baseUrl; + + $promise = $browser->request('OPTIONS', $optionsUrl); + $response = await(timeout($promise, STREAMABLE_HTTP_PROCESS_TIMEOUT - 2)); + + expect($response->getStatusCode())->toBe(204); + expect($response->getHeaderLine('Access-Control-Allow-Origin'))->toBe('*'); + expect($response->getHeaderLine('Access-Control-Allow-Methods'))->toContain('POST'); + expect($response->getHeaderLine('Access-Control-Allow-Methods'))->toContain('GET'); + expect($response->getHeaderLine('Access-Control-Allow-Headers'))->toContain('Mcp-Session-Id'); +})->group('integration', 'streamable_http'); + +it('returns 404 for unknown paths', function () { + $this->process = new Process($this->jsonModeCommand, getcwd() ?: null, null, []); + $this->process->start(); + $this->jsonClient = new MockJsonHttpClient(STREAMABLE_HTTP_HOST, $this->port, STREAMABLE_MCP_PATH); + await(delay(0.1)); + + $browser = new Browser(); + $unknownUrl = "http://" . STREAMABLE_HTTP_HOST . ":" . $this->port . "/completely/unknown/path"; + + $promise = $browser->get($unknownUrl); + + try { + await(timeout($promise, STREAMABLE_HTTP_PROCESS_TIMEOUT - 2)); + $this->fail("Request to unknown path should have failed with 404."); + } catch (ResponseException $e) { + expect($e->getResponse()->getStatusCode())->toBe(404); + $decodedBody = json_decode((string)$e->getResponse()->getBody(), true); + expect($decodedBody['error']['message'])->toContain('Not found'); + } +})->group('integration', 'streamable_http'); + +it('can delete client session with DELETE request', function () { + $this->process = new Process($this->jsonModeCommand, getcwd() ?: null, null, []); + $this->process->start(); + $this->jsonClient = new MockJsonHttpClient(STREAMABLE_HTTP_HOST, $this->port, STREAMABLE_MCP_PATH); + await(delay(0.1)); + + // 1. Initialize + await($this->jsonClient->sendRequest('initialize', ['protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, 'clientInfo' => ['name' => 'JsonModeClient', 'version' => '1.0'], 'capabilities' => []], 'init-delete-test')); + $sessionIdForDelete = $this->jsonClient->sessionId; + expect($sessionIdForDelete)->toBeString(); + await($this->jsonClient->sendNotification('notifications/initialized')); + + // 2. Establish a GET SSE connection + $sseUrl = $this->jsonClient->baseUrl; + $browserForSse = (new Browser())->withTimeout(3); + $ssePromise = $browserForSse->requestStreaming('GET', $sseUrl, [ + 'Accept' => 'text/event-stream', + 'Mcp-Session-Id' => $sessionIdForDelete + ]); + $ssePsrResponse = await(timeout($ssePromise, 3)); + expect($ssePsrResponse->getStatusCode())->toBe(200); + expect($ssePsrResponse->getHeaderLine('Content-Type'))->toBe('text/event-stream'); + + $sseStream = $ssePsrResponse->getBody(); + assert($sseStream instanceof ReadableStreamInterface); + + $isSseStreamClosed = false; + $sseStream->on('close', function () use (&$isSseStreamClosed) { + $isSseStreamClosed = true; + }); + + // 3. Send DELETE request + $deleteResponse = await($this->jsonClient->sendDeleteRequest()); + expect($deleteResponse['statusCode'])->toBe(204); + + // 4. Assert that the GET SSE stream was closed + await(delay(0.1)); + expect($isSseStreamClosed)->toBeTrue("The GET SSE stream for session {$sessionIdForDelete} was not closed after DELETE request."); + + // 5. Assert that the client session was deleted + try { + await($this->jsonClient->sendRequest('tools/list', [], 'tool-list-json-1')); + $this->fail("Expected request to tools/list to fail with 400, but it succeeded."); + } catch (ResponseException $e) { + expect($e->getResponse()->getStatusCode())->toBe(404); + $bodyContent = (string) $e->getResponse()->getBody(); + $decodedBody = json_decode($bodyContent, true); + expect($decodedBody['error']['code'])->toBe(-32600); + expect($decodedBody['error']['message'])->toContain('Invalid or expired session'); + } +})->group('integration', 'streamable_http_json'); diff --git a/vendor/php-mcp/server/tests/Mocks/Clients/MockJsonHttpClient.php b/vendor/php-mcp/server/tests/Mocks/Clients/MockJsonHttpClient.php new file mode 100644 index 000000000..34bfbc2b9 --- /dev/null +++ b/vendor/php-mcp/server/tests/Mocks/Clients/MockJsonHttpClient.php @@ -0,0 +1,114 @@ +browser = (new Browser())->withTimeout($timeout); + $this->baseUrl = "http://{$host}:{$port}/{$mcpPath}"; + } + + public function sendRequest(string $method, array $params = [], ?string $id = null): PromiseInterface + { + $payload = ['jsonrpc' => '2.0', 'method' => $method, 'params' => $params]; + if ($id !== null) { + $payload['id'] = $id; + } + + $headers = ['Content-Type' => 'application/json', 'Accept' => 'application/json, text/event-stream']; + if ($this->sessionId && $method !== 'initialize') { + $headers['Mcp-Session-Id'] = $this->sessionId; + } + + $body = json_encode($payload); + + return $this->browser->post($this->baseUrl, $headers, $body) + ->then(function (ResponseInterface $response) use ($method) { + $bodyContent = (string) $response->getBody()->getContents(); + $statusCode = $response->getStatusCode(); + + if ($method === 'initialize' && $statusCode === 200) { + $this->sessionId = $response->getHeaderLine('Mcp-Session-Id'); + } + + if ($statusCode === 202) { + if ($bodyContent !== '') { + throw new \RuntimeException("Expected empty body for 202 response, got: {$bodyContent}"); + } + return ['statusCode' => $statusCode, 'body' => null, 'headers' => $response->getHeaders()]; + } + + try { + $decoded = json_decode($bodyContent, true, 512, JSON_THROW_ON_ERROR); + return ['statusCode' => $statusCode, 'body' => $decoded, 'headers' => $response->getHeaders()]; + } catch (\JsonException $e) { + throw new \RuntimeException("Failed to decode JSON response body: {$bodyContent} Error: {$e->getMessage()}", $statusCode, $e); + } + }); + } + + public function sendBatchRequest(array $batchRequestObjects): PromiseInterface + { + $headers = ['Content-Type' => 'application/json', 'Accept' => 'application/json']; + if ($this->sessionId) { + $headers['Mcp-Session-Id'] = $this->sessionId; + } + $body = json_encode($batchRequestObjects); + + return $this->browser->post($this->baseUrl, $headers, $body) + ->then(function (ResponseInterface $response) { + $bodyContent = (string) $response->getBody()->getContents(); + $statusCode = $response->getStatusCode(); + if ($statusCode === 202) { + if ($bodyContent !== '') { + throw new \RuntimeException("Expected empty body for 202 response, got: {$bodyContent}"); + } + return ['statusCode' => $statusCode, 'body' => null, 'headers' => $response->getHeaders()]; + } + + try { + $decoded = json_decode($bodyContent, true, 512, JSON_THROW_ON_ERROR); + return ['statusCode' => $statusCode, 'body' => $decoded, 'headers' => $response->getHeaders()]; + } catch (\JsonException $e) { + throw new \RuntimeException("Failed to decode JSON response body: {$bodyContent} Error: {$e->getMessage()}", $statusCode, $e); + } + }); + } + + public function sendDeleteRequest(): PromiseInterface + { + $headers = ['Content-Type' => 'application/json', 'Accept' => 'application/json']; + if ($this->sessionId) { + $headers['Mcp-Session-Id'] = $this->sessionId; + } + + return $this->browser->delete($this->baseUrl, $headers) + ->then(function (ResponseInterface $response) { + $bodyContent = (string) $response->getBody()->getContents(); + $statusCode = $response->getStatusCode(); + return ['statusCode' => $statusCode, 'body' => $bodyContent, 'headers' => $response->getHeaders()]; + }); + } + + public function sendNotification(string $method, array $params = []): PromiseInterface + { + return $this->sendRequest($method, $params, null); + } + + public function connectSseForNotifications(): PromiseInterface + { + return resolve(null); + } +} diff --git a/vendor/php-mcp/server/tests/Mocks/Clients/MockSseClient.php b/vendor/php-mcp/server/tests/Mocks/Clients/MockSseClient.php new file mode 100644 index 000000000..ad543744c --- /dev/null +++ b/vendor/php-mcp/server/tests/Mocks/Clients/MockSseClient.php @@ -0,0 +1,236 @@ +browser = (new Browser())->withTimeout($timeout); + } + + public function connect(string $sseBaseUrl): PromiseInterface + { + return $this->browser->requestStreaming('GET', $sseBaseUrl) + ->then(function (ResponseInterface $response) { + if ($response->getStatusCode() !== 200) { + $body = (string) $response->getBody(); + throw new \RuntimeException("SSE connection failed with status {$response->getStatusCode()}: {$body}"); + } + $stream = $response->getBody(); + assert($stream instanceof ReadableStreamInterface, "SSE response body is not a readable stream"); + $this->stream = $stream; + $this->stream->on('data', [$this, 'handleSseData']); + $this->stream->on('close', function () { + $this->stream = null; + }); + return $this; + }); + } + + public function handleSseData(string $chunk): void + { + $this->buffer .= $chunk; + + while (($eventPos = strpos($this->buffer, "\n\n")) !== false) { + $eventBlock = substr($this->buffer, 0, $eventPos); + $this->buffer = substr($this->buffer, $eventPos + 2); + + $lines = explode("\n", $eventBlock); + $event = ['type' => 'message', 'data' => '', 'id' => null]; + + foreach ($lines as $line) { + if (str_starts_with($line, "event:")) { + $event['type'] = trim(substr($line, strlen("event:"))); + } elseif (str_starts_with($line, "data:")) { + $event['data'] .= (empty($event['data']) ? "" : "\n") . trim(substr($line, strlen("data:"))); + } elseif (str_starts_with($line, "id:")) { + $event['id'] = trim(substr($line, strlen("id:"))); + } + } + $this->receivedSseEvents[] = $event; + + if ($event['type'] === 'endpoint' && $event['data']) { + $this->endpointUrl = $event['data']; + $query = parse_url($this->endpointUrl, PHP_URL_QUERY); + if ($query) { + parse_str($query, $params); + $this->clientId = $params['clientId'] ?? null; + } + } elseif ($event['type'] === 'message' && $event['data']) { + try { + $decodedJson = json_decode($event['data'], true, 512, JSON_THROW_ON_ERROR); + $this->receivedMessages[] = $decodedJson; + } catch (\JsonException $e) { + } + } + } + } + + public function getNextMessageResponse(string $expectedRequestId, int $timeoutSecs = 2): PromiseInterface + { + $deferred = new Deferred(); + $startTime = microtime(true); + + $checkMessages = null; + $checkMessages = function () use (&$checkMessages, $deferred, $expectedRequestId, $startTime, $timeoutSecs) { + foreach ($this->receivedMessages as $i => $msg) { + if (isset($msg['id']) && $msg['id'] === $expectedRequestId) { + unset($this->receivedMessages[$i]); // Consume message + $this->receivedMessages = array_values($this->receivedMessages); + $deferred->resolve($msg); + return; + } + } + + if (microtime(true) - $startTime > $timeoutSecs) { + $deferred->reject(new \RuntimeException("Timeout waiting for SSE message with ID '{$expectedRequestId}'")); + return; + } + + if ($this->stream) { + Loop::addTimer(0.05, $checkMessages); + } else { + $deferred->reject(new \RuntimeException("SSE Stream closed while waiting for message ID '{$expectedRequestId}'")); + } + }; + + $checkMessages(); // Start checking + return $deferred->promise(); + } + + public function getNextBatchMessageResponse(int $expectedItemCount, int $timeoutSecs = 2): PromiseInterface + { + $deferred = new Deferred(); + $startTime = microtime(true); + + $checkMessages = null; + $checkMessages = function () use (&$checkMessages, $deferred, $expectedItemCount, $startTime, $timeoutSecs) { + foreach ($this->receivedMessages as $i => $msg) { + if (is_array($msg) && !isset($msg['jsonrpc']) && count($msg) === $expectedItemCount) { + $isLikelyBatchResponse = true; + if (empty($msg) && $expectedItemCount === 0) { + } elseif (empty($msg) && $expectedItemCount > 0) { + $isLikelyBatchResponse = false; + } else { + foreach ($msg as $item) { + if (!is_array($item) || (!isset($item['id']) && !isset($item['method']))) { + $isLikelyBatchResponse = false; + break; + } + } + } + + if ($isLikelyBatchResponse) { + unset($this->receivedMessages[$i]); + $this->receivedMessages = array_values($this->receivedMessages); + $deferred->resolve($msg); + return; + } + } + } + + if (microtime(true) - $startTime > $timeoutSecs) { + $deferred->reject(new \RuntimeException("Timeout waiting for SSE Batch Response with {$expectedItemCount} items.")); + return; + } + + if ($this->stream) { + Loop::addTimer(0.05, $checkMessages); + } else { + $deferred->reject(new \RuntimeException("SSE Stream closed while waiting for Batch Response.")); + } + }; + + $checkMessages(); + return $deferred->promise(); + } + + public function sendHttpRequest(string $requestId, string $method, array $params = []): PromiseInterface + { + if (!$this->endpointUrl || !$this->clientId) { + return reject(new \LogicException("SSE Client not fully initialized (endpoint or clientId missing).")); + } + $payload = [ + 'jsonrpc' => '2.0', + 'id' => $requestId, + 'method' => $method, + 'params' => $params, + ]; + $body = json_encode($payload); + + return $this->browser->post($this->endpointUrl, ['Content-Type' => 'application/json'], $body) + ->then(function (ResponseInterface $response) use ($requestId) { + $bodyContent = (string) $response->getBody(); + if ($response->getStatusCode() !== 202) { + throw new \RuntimeException("HTTP POST request failed with status {$response->getStatusCode()}: {$bodyContent}"); + } + return $response; + }); + } + + public function sendHttpBatchRequest(array $batchRequestObjects): PromiseInterface + { + if (!$this->endpointUrl || !$this->clientId) { + return reject(new \LogicException("SSE Client not fully initialized (endpoint or clientId missing).")); + } + $body = json_encode($batchRequestObjects); + + return $this->browser->post($this->endpointUrl, ['Content-Type' => 'application/json'], $body) + ->then(function (ResponseInterface $response) { + $bodyContent = (string) $response->getBody(); + if ($response->getStatusCode() !== 202) { + throw new \RuntimeException("HTTP BATCH POST request failed with status {$response->getStatusCode()}: {$bodyContent}"); + } + return $response; + }); + } + + public function sendHttpNotification(string $method, array $params = []): PromiseInterface + { + if (!$this->endpointUrl || !$this->clientId) { + return reject(new \LogicException("SSE Client not fully initialized (endpoint or clientId missing).")); + } + $payload = [ + 'jsonrpc' => '2.0', + 'method' => $method, + 'params' => $params, + ]; + $body = json_encode($payload); + return $this->browser->post($this->endpointUrl, ['Content-Type' => 'application/json'], $body) + ->then(function (ResponseInterface $response) { + $bodyContent = (string) $response->getBody(); + if ($response->getStatusCode() !== 202) { + throw new \RuntimeException("HTTP POST notification failed with status {$response->getStatusCode()}: {$bodyContent}"); + } + return null; + }); + } + + public function close(): void + { + if ($this->stream) { + $this->stream->close(); + $this->stream = null; + } + } +} diff --git a/vendor/php-mcp/server/tests/Mocks/Clients/MockStreamHttpClient.php b/vendor/php-mcp/server/tests/Mocks/Clients/MockStreamHttpClient.php new file mode 100644 index 000000000..1d0f47d91 --- /dev/null +++ b/vendor/php-mcp/server/tests/Mocks/Clients/MockStreamHttpClient.php @@ -0,0 +1,252 @@ +browser = (new Browser())->withTimeout($timeout); + $this->baseMcpUrl = "http://{$host}:{$port}/{$mcpPath}"; + } + + public function connectMainSseStream(): PromiseInterface + { + if (!$this->sessionId) { + return reject(new \LogicException("Cannot connect main SSE stream without a session ID. Initialize first.")); + } + + return $this->browser->requestStreaming('GET', $this->baseMcpUrl, [ + 'Accept' => 'text/event-stream', + 'Mcp-Session-Id' => $this->sessionId + ]) + ->then(function (ResponseInterface $response) { + if ($response->getStatusCode() !== 200) { + $body = (string) $response->getBody(); + throw new \RuntimeException("Main SSE GET connection failed with status {$response->getStatusCode()}: {$body}"); + } + $stream = $response->getBody(); + assert($stream instanceof ReadableStreamInterface); + $this->mainSseGetStream = $stream; + + $this->mainSseGetStream->on('data', function ($chunk) { + $this->mainSseGetBuffer .= $chunk; + $this->processBufferForNotifications($this->mainSseGetBuffer, $this->mainSseReceivedNotifications); + }); + return $this; + }); + } + + private function processBufferForNotifications(string &$buffer, array &$targetArray): void + { + while (($eventPos = strpos($buffer, "\n\n")) !== false) { + $eventBlock = substr($buffer, 0, $eventPos); + $buffer = substr($buffer, $eventPos + 2); + $lines = explode("\n", $eventBlock); + $eventData = ''; + foreach ($lines as $line) { + if (str_starts_with($line, "data:")) { + $eventData .= (empty($eventData) ? "" : "\n") . trim(substr($line, strlen("data:"))); + } + } + if (!empty($eventData)) { + try { + $decodedJson = json_decode($eventData, true, 512, JSON_THROW_ON_ERROR); + if (isset($decodedJson['method']) && str_starts_with($decodedJson['method'], 'notifications/')) { + $targetArray[] = $decodedJson; + } + } catch (\JsonException $e) { /* ignore non-json data lines or log */ + } + } + } + } + + + public function sendInitializeRequest(array $params, string $id = 'init-stream-1'): PromiseInterface + { + $payload = ['jsonrpc' => '2.0', 'method' => 'initialize', 'params' => $params, 'id' => $id]; + $headers = ['Content-Type' => 'application/json', 'Accept' => 'text/event-stream']; + $body = json_encode($payload); + + return $this->browser->requestStreaming('POST', $this->baseMcpUrl, $headers, $body) + ->then(function (ResponseInterface $response) use ($id) { + $statusCode = $response->getStatusCode(); + + if ($statusCode !== 200 || !str_contains($response->getHeaderLine('Content-Type'), 'text/event-stream')) { + throw new \RuntimeException("Initialize POST failed or did not return SSE stream. Status: {$statusCode}"); + } + + $this->sessionId = $response->getHeaderLine('Mcp-Session-Id'); + + $stream = $response->getBody(); + assert($stream instanceof ReadableStreamInterface); + return $this->collectSingleSseResponse($stream, $id, "Initialize"); + }); + } + + public function sendRequest(string $method, array $params, string $id): PromiseInterface + { + $payload = ['jsonrpc' => '2.0', 'method' => $method, 'params' => $params, 'id' => $id]; + $headers = ['Content-Type' => 'application/json', 'Accept' => 'text/event-stream']; + if ($this->sessionId) $headers['Mcp-Session-Id'] = $this->sessionId; + + $body = json_encode($payload); + + return $this->browser->requestStreaming('POST', $this->baseMcpUrl, $headers, $body) + ->then(function (ResponseInterface $response) use ($id, $method) { + $statusCode = $response->getStatusCode(); + + if ($statusCode !== 200 || !str_contains($response->getHeaderLine('Content-Type'), 'text/event-stream')) { + $bodyContent = (string) $response->getBody(); + throw new \RuntimeException("Request '{$method}' (ID: {$id}) POST failed or did not return SSE stream. Status: {$statusCode}, Body: {$bodyContent}"); + } + + $stream = $response->getBody(); + assert($stream instanceof ReadableStreamInterface); + return $this->collectSingleSseResponse($stream, $id, $method); + }); + } + + public function sendBatchRequest(array $batchPayload): PromiseInterface + { + if (!$this->sessionId) { + return reject(new \LogicException("Session ID not set. Initialize first for batch request.")); + } + + $headers = ['Content-Type' => 'application/json', 'Accept' => 'text/event-stream', 'Mcp-Session-Id' => $this->sessionId]; + $body = json_encode($batchPayload); + + return $this->browser->requestStreaming('POST', $this->baseMcpUrl, $headers, $body) + ->then(function (ResponseInterface $response) { + $statusCode = $response->getStatusCode(); + + if ($statusCode !== 200 || !str_contains($response->getHeaderLine('Content-Type'), 'text/event-stream')) { + throw new \RuntimeException("Batch POST failed or did not return SSE stream. Status: {$statusCode}"); + } + + $stream = $response->getBody(); + assert($stream instanceof ReadableStreamInterface); + return $this->collectSingleSseResponse($stream, null, "Batch", true); + }); + } + + private function collectSingleSseResponse(ReadableStreamInterface $stream, ?string $expectedRequestId, string $contextHint, bool $expectBatchArray = false): PromiseInterface + { + $deferred = new Deferred(); + $buffer = ''; + $streamClosed = false; + + $dataListener = function ($chunk) use (&$buffer, $deferred, $expectedRequestId, $expectBatchArray, $contextHint, &$streamClosed, &$dataListener, $stream) { + if ($streamClosed) return; + $buffer .= $chunk; + + if (str_contains($buffer, "event: message\n")) { + if (preg_match('/data: (.*)\n\n/s', $buffer, $matches)) { + $jsonData = trim($matches[1]); + + try { + $decoded = json_decode($jsonData, true, 512, JSON_THROW_ON_ERROR); + $isValid = false; + if ($expectBatchArray) { + $isValid = is_array($decoded) && !isset($decoded['jsonrpc']); + } else { + $isValid = isset($decoded['id']) && $decoded['id'] === $expectedRequestId; + } + + if ($isValid) { + $deferred->resolve($decoded); + $stream->removeListener('data', $dataListener); + $stream->close(); + return; + } + } catch (\JsonException $e) { + $deferred->reject(new \RuntimeException("SSE JSON decode failed for {$contextHint}: {$jsonData}", 0, $e)); + $stream->removeListener('data', $dataListener); + $stream->close(); + return; + } + } + } + }; + + $stream->on('data', $dataListener); + $stream->on('close', function () use ($deferred, $contextHint, &$streamClosed) { + $streamClosed = true; + $deferred->reject(new \RuntimeException("SSE stream for {$contextHint} closed before expected response was received.")); + }); + $stream->on('error', function ($err) use ($deferred, $contextHint, &$streamClosed) { + $streamClosed = true; + $deferred->reject(new \RuntimeException("SSE stream error for {$contextHint}.", 0, $err instanceof \Throwable ? $err : null)); + }); + + return timeout($deferred->promise(), 2, Loop::get()) + ->finally(function () use ($stream, $dataListener) { + if ($stream->isReadable()) { + $stream->removeListener('data', $dataListener); + } + }); + } + + public function sendHttpNotification(string $method, array $params = []): PromiseInterface + { + if (!$this->sessionId) { + return reject(new \LogicException("Session ID not set for notification. Initialize first.")); + } + $payload = ['jsonrpc' => '2.0', 'method' => $method, 'params' => $params]; + $headers = ['Content-Type' => 'application/json', 'Accept' => 'application/json', 'Mcp-Session-Id' => $this->sessionId]; + $body = json_encode($payload); + + return $this->browser->post($this->baseMcpUrl, $headers, $body) + ->then(function (ResponseInterface $response) { + $statusCode = $response->getStatusCode(); + + if ($statusCode !== 202) { + throw new \RuntimeException("POST Notification failed with status {$statusCode}: " . (string)$response->getBody()); + } + + return ['statusCode' => $statusCode, 'body' => null]; + }); + } + + public function sendDeleteRequest(): PromiseInterface + { + if (!$this->sessionId) { + return reject(new \LogicException("Session ID not set for DELETE request. Initialize first.")); + } + + $headers = ['Mcp-Session-Id' => $this->sessionId]; + + return $this->browser->request('DELETE', $this->baseMcpUrl, $headers) + ->then(function (ResponseInterface $response) { + $statusCode = $response->getStatusCode(); + return ['statusCode' => $statusCode, 'body' => (string)$response->getBody()]; + }); + } + + public function closeMainSseStream(): void + { + if ($this->mainSseGetStream) { + $this->mainSseGetStream->close(); + $this->mainSseGetStream = null; + } + } +} diff --git a/vendor/php-mcp/server/tests/Mocks/Clock/FixedClock.php b/vendor/php-mcp/server/tests/Mocks/Clock/FixedClock.php new file mode 100644 index 000000000..24b427138 --- /dev/null +++ b/vendor/php-mcp/server/tests/Mocks/Clock/FixedClock.php @@ -0,0 +1,78 @@ +currentTime = $initialTime; + } else { + $this->currentTime = new DateTimeImmutable($initialTime, $timezone); + } + } + + public function now(): DateTimeImmutable + { + return $this->currentTime; + } + + public function setCurrentTime(string|DateTimeImmutable $newTime, ?DateTimeZone $timezone = null): void + { + if ($newTime instanceof DateTimeImmutable) { + $this->currentTime = $newTime; + } else { + $this->currentTime = new DateTimeImmutable($newTime, $timezone); + } + } + + public function advance(DateInterval $interval): void + { + $this->currentTime = $this->currentTime->add($interval); + } + + public function rewind(DateInterval $interval): void + { + $this->currentTime = $this->currentTime->sub($interval); + } + + public function addSecond(): void + { + $this->advance(new DateInterval("PT1S")); + } + + public function addSeconds(int $seconds): void + { + $this->advance(new DateInterval("PT{$seconds}S")); + } + + public function addMinutes(int $minutes): void + { + $this->advance(new DateInterval("PT{$minutes}M")); + } + + public function addHours(int $hours): void + { + $this->advance(new DateInterval("PT{$hours}H")); + } + + public function subSeconds(int $seconds): void + { + $this->rewind(new DateInterval("PT{$seconds}S")); + } + + public function subMinutes(int $minutes): void + { + $this->rewind(new DateInterval("PT{$minutes}M")); + } +} diff --git a/vendor/php-mcp/server/tests/Pest.php b/vendor/php-mcp/server/tests/Pest.php new file mode 100644 index 000000000..5020906a4 --- /dev/null +++ b/vendor/php-mcp/server/tests/Pest.php @@ -0,0 +1,94 @@ +getProperty($propertyName); + $property->setAccessible(true); + return $property->getValue($object); +} + +function delay($time, ?LoopInterface $loop = null) +{ + if ($loop === null) { + $loop = Loop::get(); + } + + /** @var TimerInterface $timer */ + $timer = null; + return new Promise(function ($resolve) use ($loop, $time, &$timer) { + $timer = $loop->addTimer($time, function () use ($resolve) { + $resolve(null); + }); + }, function () use (&$timer, $loop) { + $loop->cancelTimer($timer); + $timer = null; + + throw new \RuntimeException('Timer cancelled'); + }); +} + +function timeout(PromiseInterface $promise, $time, ?LoopInterface $loop = null) +{ + $canceller = null; + if (\method_exists($promise, 'cancel')) { + $canceller = function () use (&$promise) { + $promise->cancel(); + $promise = null; + }; + } + + if ($loop === null) { + $loop = Loop::get(); + } + + return new Promise(function ($resolve, $reject) use ($loop, $time, $promise) { + $timer = null; + $promise = $promise->then(function ($v) use (&$timer, $loop, $resolve) { + if ($timer) { + $loop->cancelTimer($timer); + } + $timer = false; + $resolve($v); + }, function ($v) use (&$timer, $loop, $reject) { + if ($timer) { + $loop->cancelTimer($timer); + } + $timer = false; + $reject($v); + }); + + if ($timer === false) { + return; + } + + // start timeout timer which will cancel the input promise + $timer = $loop->addTimer($time, function () use ($time, &$promise, $reject) { + $reject(new \RuntimeException('Timed out after ' . $time . ' seconds')); + + if (\method_exists($promise, 'cancel')) { + $promise->cancel(); + } + $promise = null; + }); + }, $canceller); +} + +function findFreePort() +{ + $server = new SocketServer('127.0.0.1:0'); + $address = $server->getAddress(); + $port = $address ? parse_url($address, PHP_URL_PORT) : null; + $server->close(); + if (!$port) { + throw new \RuntimeException("Could not find a free port for testing."); + } + return (int)$port; +} diff --git a/vendor/php-mcp/server/tests/TestCase.php b/vendor/php-mcp/server/tests/TestCase.php new file mode 100644 index 000000000..612a94e5c --- /dev/null +++ b/vendor/php-mcp/server/tests/TestCase.php @@ -0,0 +1,10 @@ +provider)->toBe(CompletionProviderFixture::class); + expect($attribute->values)->toBeNull(); + expect($attribute->enum)->toBeNull(); +}); + +it('can be constructed with provider instance', function () { + $instance = new CompletionProviderFixture(); + $attribute = new CompletionProvider(provider: $instance); + + expect($attribute->provider)->toBe($instance); + expect($attribute->values)->toBeNull(); + expect($attribute->enum)->toBeNull(); +}); + +it('can be constructed with values array', function () { + $values = ['draft', 'published', 'archived']; + $attribute = new CompletionProvider(values: $values); + + expect($attribute->provider)->toBeNull(); + expect($attribute->values)->toBe($values); + expect($attribute->enum)->toBeNull(); +}); + +it('can be constructed with enum class', function () { + $attribute = new CompletionProvider(enum: StatusEnum::class); + + expect($attribute->provider)->toBeNull(); + expect($attribute->values)->toBeNull(); + expect($attribute->enum)->toBe(StatusEnum::class); +}); + +it('throws exception when no parameters provided', function () { + new CompletionProvider(); +})->throws(\InvalidArgumentException::class, 'Only one of provider, values, or enum can be set'); + +it('throws exception when multiple parameters provided', function () { + new CompletionProvider( + provider: CompletionProviderFixture::class, + values: ['test'] + ); +})->throws(\InvalidArgumentException::class, 'Only one of provider, values, or enum can be set'); + +it('throws exception when all parameters provided', function () { + new CompletionProvider( + provider: CompletionProviderFixture::class, + values: ['test'], + enum: StatusEnum::class + ); +})->throws(\InvalidArgumentException::class, 'Only one of provider, values, or enum can be set'); diff --git a/vendor/php-mcp/server/tests/Unit/Attributes/McpPromptTest.php b/vendor/php-mcp/server/tests/Unit/Attributes/McpPromptTest.php new file mode 100644 index 000000000..922404c61 --- /dev/null +++ b/vendor/php-mcp/server/tests/Unit/Attributes/McpPromptTest.php @@ -0,0 +1,36 @@ +name)->toBe($name); + expect($attribute->description)->toBe($description); +}); + +it('instantiates with null values for name and description', function () { + // Arrange & Act + $attribute = new McpPrompt(name: null, description: null); + + // Assert + expect($attribute->name)->toBeNull(); + expect($attribute->description)->toBeNull(); +}); + +it('instantiates with missing optional arguments', function () { + // Arrange & Act + $attribute = new McpPrompt(); // Use default constructor values + + // Assert + expect($attribute->name)->toBeNull(); + expect($attribute->description)->toBeNull(); +}); diff --git a/vendor/php-mcp/server/tests/Unit/Attributes/McpResourceTemplateTest.php b/vendor/php-mcp/server/tests/Unit/Attributes/McpResourceTemplateTest.php new file mode 100644 index 000000000..37e70f9cb --- /dev/null +++ b/vendor/php-mcp/server/tests/Unit/Attributes/McpResourceTemplateTest.php @@ -0,0 +1,55 @@ +uriTemplate)->toBe($uriTemplate); + expect($attribute->name)->toBe($name); + expect($attribute->description)->toBe($description); + expect($attribute->mimeType)->toBe($mimeType); +}); + +it('instantiates with null values for name and description', function () { + // Arrange & Act + $attribute = new McpResourceTemplate( + uriTemplate: 'test://{id}', // uriTemplate is required + name: null, + description: null, + mimeType: null, + ); + + // Assert + expect($attribute->uriTemplate)->toBe('test://{id}'); + expect($attribute->name)->toBeNull(); + expect($attribute->description)->toBeNull(); + expect($attribute->mimeType)->toBeNull(); +}); + +it('instantiates with missing optional arguments', function () { + // Arrange & Act + $uriTemplate = 'tmpl://{key}'; + $attribute = new McpResourceTemplate(uriTemplate: $uriTemplate); + + // Assert + expect($attribute->uriTemplate)->toBe($uriTemplate); + expect($attribute->name)->toBeNull(); + expect($attribute->description)->toBeNull(); + expect($attribute->mimeType)->toBeNull(); +}); diff --git a/vendor/php-mcp/server/tests/Unit/Attributes/McpResourceTest.php b/vendor/php-mcp/server/tests/Unit/Attributes/McpResourceTest.php new file mode 100644 index 000000000..be025fe9e --- /dev/null +++ b/vendor/php-mcp/server/tests/Unit/Attributes/McpResourceTest.php @@ -0,0 +1,61 @@ +uri)->toBe($uri); + expect($attribute->name)->toBe($name); + expect($attribute->description)->toBe($description); + expect($attribute->mimeType)->toBe($mimeType); + expect($attribute->size)->toBe($size); +}); + +it('instantiates with null values for name and description', function () { + // Arrange & Act + $attribute = new McpResource( + uri: 'file:///test', // URI is required + name: null, + description: null, + mimeType: null, + size: null, + ); + + // Assert + expect($attribute->uri)->toBe('file:///test'); + expect($attribute->name)->toBeNull(); + expect($attribute->description)->toBeNull(); + expect($attribute->mimeType)->toBeNull(); + expect($attribute->size)->toBeNull(); +}); + +it('instantiates with missing optional arguments', function () { + // Arrange & Act + $uri = 'file:///only-uri'; + $attribute = new McpResource(uri: $uri); + + // Assert + expect($attribute->uri)->toBe($uri); + expect($attribute->name)->toBeNull(); + expect($attribute->description)->toBeNull(); + expect($attribute->mimeType)->toBeNull(); + expect($attribute->size)->toBeNull(); +}); diff --git a/vendor/php-mcp/server/tests/Unit/Attributes/McpToolTest.php b/vendor/php-mcp/server/tests/Unit/Attributes/McpToolTest.php new file mode 100644 index 000000000..4f48ba730 --- /dev/null +++ b/vendor/php-mcp/server/tests/Unit/Attributes/McpToolTest.php @@ -0,0 +1,36 @@ +name)->toBe($name); + expect($attribute->description)->toBe($description); +}); + +it('instantiates with null values for name and description', function () { + // Arrange & Act + $attribute = new McpTool(name: null, description: null); + + // Assert + expect($attribute->name)->toBeNull(); + expect($attribute->description)->toBeNull(); +}); + +it('instantiates with missing optional arguments', function () { + // Arrange & Act + $attribute = new McpTool(); // Use default constructor values + + // Assert + expect($attribute->name)->toBeNull(); + expect($attribute->description)->toBeNull(); +}); diff --git a/vendor/php-mcp/server/tests/Unit/ConfigurationTest.php b/vendor/php-mcp/server/tests/Unit/ConfigurationTest.php new file mode 100644 index 000000000..57112e0a3 --- /dev/null +++ b/vendor/php-mcp/server/tests/Unit/ConfigurationTest.php @@ -0,0 +1,92 @@ +serverInfo = Implementation::make('TestServer', '1.1.0'); + $this->logger = Mockery::mock(LoggerInterface::class); + $this->loop = Mockery::mock(LoopInterface::class); + $this->cache = Mockery::mock(CacheInterface::class); + $this->container = Mockery::mock(ContainerInterface::class); + $this->capabilities = ServerCapabilities::make(); +}); + +afterEach(function () { + Mockery::close(); +}); + +it('constructs configuration object with all properties', function () { + $paginationLimit = 100; + $config = new Configuration( + serverInfo: $this->serverInfo, + capabilities: $this->capabilities, + logger: $this->logger, + loop: $this->loop, + cache: $this->cache, + container: $this->container, + paginationLimit: $paginationLimit + ); + + expect($config->serverInfo)->toBe($this->serverInfo); + expect($config->capabilities)->toBe($this->capabilities); + expect($config->logger)->toBe($this->logger); + expect($config->loop)->toBe($this->loop); + expect($config->cache)->toBe($this->cache); + expect($config->container)->toBe($this->container); + expect($config->paginationLimit)->toBe($paginationLimit); +}); + +it('constructs configuration object with default pagination limit', function () { + $config = new Configuration( + serverInfo: $this->serverInfo, + capabilities: $this->capabilities, + logger: $this->logger, + loop: $this->loop, + cache: $this->cache, + container: $this->container + ); + + expect($config->paginationLimit)->toBe(50); // Default value +}); + +it('constructs configuration object with null cache', function () { + $config = new Configuration( + serverInfo: $this->serverInfo, + capabilities: $this->capabilities, + logger: $this->logger, + loop: $this->loop, + cache: null, + container: $this->container + ); + + expect($config->cache)->toBeNull(); +}); + +it('constructs configuration object with specific capabilities', function () { + $customCaps = ServerCapabilities::make( + resourcesSubscribe: true, + logging: true, + ); + + $config = new Configuration( + serverInfo: $this->serverInfo, + capabilities: $customCaps, + logger: $this->logger, + loop: $this->loop, + cache: null, + container: $this->container + ); + + expect($config->capabilities)->toBe($customCaps); + expect($config->capabilities->resourcesSubscribe)->toBeTrue(); + expect($config->capabilities->logging)->toBeTrue(); +}); diff --git a/vendor/php-mcp/server/tests/Unit/Defaults/EnumCompletionProviderTest.php b/vendor/php-mcp/server/tests/Unit/Defaults/EnumCompletionProviderTest.php new file mode 100644 index 000000000..3caf400b4 --- /dev/null +++ b/vendor/php-mcp/server/tests/Unit/Defaults/EnumCompletionProviderTest.php @@ -0,0 +1,90 @@ +session = Mockery::mock(SessionInterface::class); +}); + +it('creates provider from string-backed enum', function () { + $provider = new EnumCompletionProvider(StringEnum::class); + + $result = $provider->getCompletions('', $this->session); + + expect($result)->toBe(['draft', 'published', 'archived']); +}); + +it('creates provider from int-backed enum using names', function () { + $provider = new EnumCompletionProvider(IntEnum::class); + + $result = $provider->getCompletions('', $this->session); + + expect($result)->toBe(['LOW', 'MEDIUM', 'HIGH']); +}); + +it('creates provider from unit enum using names', function () { + $provider = new EnumCompletionProvider(UnitEnum::class); + + $result = $provider->getCompletions('', $this->session); + + expect($result)->toBe(['ALPHA', 'BETA', 'GAMMA']); +}); + +it('filters string enum values by prefix', function () { + $provider = new EnumCompletionProvider(StringEnum::class); + + $result = $provider->getCompletions('ar', $this->session); + + expect($result)->toEqual(['archived']); +}); + +it('filters unit enum values by prefix', function () { + $provider = new EnumCompletionProvider(UnitEnum::class); + + $result = $provider->getCompletions('A', $this->session); + + expect($result)->toBe(['ALPHA']); +}); + +it('returns empty array when no values match prefix', function () { + $provider = new EnumCompletionProvider(StringEnum::class); + + $result = $provider->getCompletions('xyz', $this->session); + + expect($result)->toBe([]); +}); + +it('throws exception for non-enum class', function () { + new EnumCompletionProvider(\stdClass::class); +})->throws(\InvalidArgumentException::class, 'Class stdClass is not an enum'); + +it('throws exception for non-existent class', function () { + new EnumCompletionProvider('NonExistentClass'); +})->throws(\InvalidArgumentException::class, 'Class NonExistentClass is not an enum'); diff --git a/vendor/php-mcp/server/tests/Unit/Defaults/ListCompletionProviderTest.php b/vendor/php-mcp/server/tests/Unit/Defaults/ListCompletionProviderTest.php new file mode 100644 index 000000000..741d0b8bb --- /dev/null +++ b/vendor/php-mcp/server/tests/Unit/Defaults/ListCompletionProviderTest.php @@ -0,0 +1,75 @@ +session = Mockery::mock(SessionInterface::class); +}); + +it('returns all values when current value is empty', function () { + $values = ['apple', 'banana', 'cherry']; + $provider = new ListCompletionProvider($values); + + $result = $provider->getCompletions('', $this->session); + + expect($result)->toBe($values); +}); + +it('filters values based on current value prefix', function () { + $values = ['apple', 'apricot', 'banana', 'cherry']; + $provider = new ListCompletionProvider($values); + + $result = $provider->getCompletions('ap', $this->session); + + expect($result)->toBe(['apple', 'apricot']); +}); + +it('returns empty array when no values match', function () { + $values = ['apple', 'banana', 'cherry']; + $provider = new ListCompletionProvider($values); + + $result = $provider->getCompletions('xyz', $this->session); + + expect($result)->toBe([]); +}); + +it('works with single character prefix', function () { + $values = ['apple', 'banana', 'cherry']; + $provider = new ListCompletionProvider($values); + + $result = $provider->getCompletions('a', $this->session); + + expect($result)->toBe(['apple']); +}); + +it('is case sensitive by default', function () { + $values = ['Apple', 'apple', 'APPLE']; + $provider = new ListCompletionProvider($values); + + $result = $provider->getCompletions('A', $this->session); + + expect($result)->toEqual(['Apple', 'APPLE']); +}); + +it('handles empty values array', function () { + $provider = new ListCompletionProvider([]); + + $result = $provider->getCompletions('test', $this->session); + + expect($result)->toBe([]); +}); + +it('preserves array order', function () { + $values = ['zebra', 'apple', 'banana']; + $provider = new ListCompletionProvider($values); + + $result = $provider->getCompletions('', $this->session); + + expect($result)->toBe(['zebra', 'apple', 'banana']); +}); diff --git a/vendor/php-mcp/server/tests/Unit/DispatcherTest.php b/vendor/php-mcp/server/tests/Unit/DispatcherTest.php new file mode 100644 index 000000000..79e7b15d6 --- /dev/null +++ b/vendor/php-mcp/server/tests/Unit/DispatcherTest.php @@ -0,0 +1,572 @@ +configuration = Mockery::mock(Configuration::class); + /** @var MockInterface&Registry $registry */ + $this->registry = Mockery::mock(Registry::class); + /** @var MockInterface&SubscriptionManager $subscriptionManager */ + $this->subscriptionManager = Mockery::mock(SubscriptionManager::class); + /** @var MockInterface&SchemaValidator $schemaValidator */ + $this->schemaValidator = Mockery::mock(SchemaValidator::class); + /** @var MockInterface&SessionInterface $session */ + $this->session = Mockery::mock(SessionInterface::class); + /** @var MockInterface&ContainerInterface $container */ + $this->container = Mockery::mock(ContainerInterface::class); + + $configuration = new Configuration( + serverInfo: Implementation::make('DispatcherTestServer', '1.0'), + capabilities: ServerCapabilities::make(), + paginationLimit: DISPATCHER_PAGINATION_LIMIT, + logger: new NullLogger(), + loop: Loop::get(), + cache: null, + container: $this->container + ); + + $this->dispatcher = new Dispatcher( + $configuration, + $this->registry, + $this->subscriptionManager, + $this->schemaValidator + ); +}); + +it('routes to handleInitialize for initialize request', function () { + $request = new JsonRpcRequest( + jsonrpc: '2.0', + id: 1, + method: 'initialize', + params: [ + 'protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, + 'clientInfo' => ['name' => 'client', 'version' => '1.0'], + 'capabilities' => [], + ] + ); + $this->session->shouldReceive('set')->with('client_info', Mockery::on(fn($value) => $value['name'] === 'client' && $value['version'] === '1.0'))->once(); + $this->session->shouldReceive('set')->with('protocol_version', Protocol::LATEST_PROTOCOL_VERSION)->once(); + + $result = $this->dispatcher->handleRequest($request, $this->session); + expect($result)->toBeInstanceOf(InitializeResult::class); + expect($result->protocolVersion)->toBe(Protocol::LATEST_PROTOCOL_VERSION); + expect($result->serverInfo->name)->toBe('DispatcherTestServer'); +}); + +it('routes to handlePing for ping request', function () { + $request = new JsonRpcRequest('2.0', 'id1', 'ping', []); + $result = $this->dispatcher->handleRequest($request, $this->session); + expect($result)->toBeInstanceOf(EmptyResult::class); +}); + +it('throws MethodNotFound for unknown request method', function () { + $rawRequest = new JsonRpcRequest('2.0', 'id1', 'unknown/method', []); + $this->dispatcher->handleRequest($rawRequest, $this->session); +})->throws(McpServerException::class, "Method 'unknown/method' not found."); + +it('routes to handleNotificationInitialized for initialized notification', function () { + $notification = new JsonRpcNotification('2.0', 'notifications/initialized', []); + $this->session->shouldReceive('set')->with('initialized', true)->once(); + $this->dispatcher->handleNotification($notification, $this->session); +}); + +it('does nothing for unknown notification method', function () { + $rawNotification = new JsonRpcNotification('2.0', 'unknown/notification', []); + $this->session->shouldNotReceive('set'); + $this->dispatcher->handleNotification($rawNotification, $this->session); +}); + + +it('can handle initialize request', function () { + $clientInfo = Implementation::make('TestClient', '0.9.9'); + $request = InitializeRequest::make(1, Protocol::LATEST_PROTOCOL_VERSION, ClientCapabilities::make(), $clientInfo, []); + $this->session->shouldReceive('set')->with('client_info', $clientInfo->toArray())->once(); + $this->session->shouldReceive('set')->with('protocol_version', Protocol::LATEST_PROTOCOL_VERSION)->once(); + + $result = $this->dispatcher->handleInitialize($request, $this->session); + expect($result->protocolVersion)->toBe(Protocol::LATEST_PROTOCOL_VERSION); + expect($result->serverInfo->name)->toBe('DispatcherTestServer'); + expect($result->capabilities)->toBeInstanceOf(ServerCapabilities::class); +}); + +it('can handle initialize request with older supported protocol version', function () { + $clientInfo = Implementation::make('TestClient', '0.9.9'); + $clientRequestedVersion = '2024-11-05'; + $request = InitializeRequest::make(1, $clientRequestedVersion, ClientCapabilities::make(), $clientInfo, []); + $this->session->shouldReceive('set')->with('client_info', $clientInfo->toArray())->once(); + $this->session->shouldReceive('set')->with('protocol_version', $clientRequestedVersion)->once(); + + $result = $this->dispatcher->handleInitialize($request, $this->session); + expect($result->protocolVersion)->toBe($clientRequestedVersion); + expect($result->serverInfo->name)->toBe('DispatcherTestServer'); + expect($result->capabilities)->toBeInstanceOf(ServerCapabilities::class); +}); + +it('can handle initialize request with unsupported protocol version', function () { + $clientInfo = Implementation::make('TestClient', '0.9.9'); + $unsupportedVersion = '1999-01-01'; + $request = InitializeRequest::make(1, $unsupportedVersion, ClientCapabilities::make(), $clientInfo, []); + $this->session->shouldReceive('set')->with('client_info', $clientInfo->toArray())->once(); + $this->session->shouldReceive('set')->with('protocol_version', Protocol::LATEST_PROTOCOL_VERSION)->once(); + + $result = $this->dispatcher->handleInitialize($request, $this->session); + expect($result->protocolVersion)->toBe(Protocol::LATEST_PROTOCOL_VERSION); + expect($result->serverInfo->name)->toBe('DispatcherTestServer'); + expect($result->capabilities)->toBeInstanceOf(ServerCapabilities::class); +}); + +it('can handle tool list request and return paginated tools', function () { + $toolSchemas = [ + ToolSchema::make('tool1', ['type' => 'object', 'properties' => []]), + ToolSchema::make('tool2', ['type' => 'object', 'properties' => []]), + ToolSchema::make('tool3', ['type' => 'object', 'properties' => []]), + ToolSchema::make('tool4', ['type' => 'object', 'properties' => []]), + ]; + $this->registry->shouldReceive('getTools')->andReturn($toolSchemas); + + $request = ListToolsRequest::make(1); + $result = $this->dispatcher->handleToolList($request); + expect($result->tools)->toHaveCount(DISPATCHER_PAGINATION_LIMIT); + expect($result->tools[0]->name)->toBe('tool1'); + expect($result->nextCursor)->toBeString(); + + $nextCursor = $result->nextCursor; + $requestPage2 = ListToolsRequest::make(2, $nextCursor); + $resultPage2 = $this->dispatcher->handleToolList($requestPage2); + expect($resultPage2->tools)->toHaveCount(count($toolSchemas) - DISPATCHER_PAGINATION_LIMIT); + expect($resultPage2->tools[0]->name)->toBe('tool4'); + expect($resultPage2->nextCursor)->toBeNull(); +}); + +it('can handle tool call request and return result', function () { + $toolName = 'my-calculator'; + $args = ['a' => 10, 'b' => 5]; + $toolSchema = ToolSchema::make($toolName, ['type' => 'object', 'properties' => ['a' => ['type' => 'integer'], 'b' => ['type' => 'integer']]]); + $registeredToolMock = Mockery::mock(RegisteredTool::class, [$toolSchema, 'MyToolHandler', 'handleTool', false]); + + $this->registry->shouldReceive('getTool')->with($toolName)->andReturn($registeredToolMock); + $this->schemaValidator->shouldReceive('validateAgainstJsonSchema')->with($args, $toolSchema->inputSchema)->andReturn([]); // No validation errors + $registeredToolMock->shouldReceive('call')->with($this->container, $args)->andReturn([TextContent::make("Result: 15")]); + + $request = CallToolRequest::make(1, $toolName, $args); + $result = $this->dispatcher->handleToolCall($request); + + expect($result)->toBeInstanceOf(CallToolResult::class); + expect($result->content[0]->text)->toBe("Result: 15"); + expect($result->isError)->toBeFalse(); +}); + +it('can handle tool call request and throw exception if tool not found', function () { + $this->registry->shouldReceive('getTool')->with('unknown-tool')->andReturn(null); + $request = CallToolRequest::make(1, 'unknown-tool', []); + $this->dispatcher->handleToolCall($request); +})->throws(McpServerException::class, "Tool 'unknown-tool' not found."); + +it('can handle tool call request and throw exception if argument validation fails', function () { + $toolName = 'strict-tool'; + $args = ['param' => 'wrong_type']; + $toolSchema = ToolSchema::make($toolName, ['type' => 'object', 'properties' => ['param' => ['type' => 'integer']]]); + $registeredToolMock = Mockery::mock(RegisteredTool::class, [$toolSchema, 'MyToolHandler', 'handleTool', false]); + + $this->registry->shouldReceive('getTool')->with($toolName)->andReturn($registeredToolMock); + $validationErrors = [['pointer' => '/param', 'keyword' => 'type', 'message' => 'Expected integer']]; + $this->schemaValidator->shouldReceive('validateAgainstJsonSchema')->with($args, $toolSchema->inputSchema)->andReturn($validationErrors); + + $request = CallToolRequest::make(1, $toolName, $args); + try { + $this->dispatcher->handleToolCall($request); + } catch (McpServerException $e) { + expect($e->getMessage())->toContain("Invalid parameters for tool 'strict-tool'"); + expect($e->getData()['validation_errors'])->toBeArray(); + } +}); + +it('can handle tool call request and return error if tool execution throws exception', function () { + $toolName = 'failing-tool'; + $toolSchema = ToolSchema::make($toolName, ['type' => 'object', 'properties' => []]); + $registeredToolMock = Mockery::mock(RegisteredTool::class, [$toolSchema, 'MyToolHandler', 'handleTool', false]); + + $this->registry->shouldReceive('getTool')->with($toolName)->andReturn($registeredToolMock); + $this->schemaValidator->shouldReceive('validateAgainstJsonSchema')->andReturn([]); + $registeredToolMock->shouldReceive('call')->andThrow(new \RuntimeException("Tool crashed!")); + + $request = CallToolRequest::make(1, $toolName, []); + $result = $this->dispatcher->handleToolCall($request); + + expect($result->isError)->toBeTrue(); + expect($result->content[0]->text)->toBe("Tool execution failed: Tool crashed!"); +}); + +it('can handle tool call request and return error if result formatting fails', function () { + $toolName = 'bad-result-tool'; + $toolSchema = ToolSchema::make($toolName, ['type' => 'object', 'properties' => []]); + $registeredToolMock = Mockery::mock(RegisteredTool::class, [$toolSchema, 'MyToolHandler', 'handleTool', false]); + + $this->registry->shouldReceive('getTool')->with($toolName)->andReturn($registeredToolMock); + $this->schemaValidator->shouldReceive('validateAgainstJsonSchema')->andReturn([]); + $registeredToolMock->shouldReceive('call')->andThrow(new \JsonException("Unencodable.")); + + + $request = CallToolRequest::make(1, $toolName, []); + $result = $this->dispatcher->handleToolCall($request); + + expect($result->isError)->toBeTrue(); + expect($result->content[0]->text)->toBe("Failed to serialize tool result: Unencodable."); +}); + + +it('can handle resources list request and return paginated resources', function () { + $resourceSchemas = [ + ResourceSchema::make('res://1', 'Resource1'), + ResourceSchema::make('res://2', 'Resource2'), + ResourceSchema::make('res://3', 'Resource3'), + ResourceSchema::make('res://4', 'Resource4'), + ResourceSchema::make('res://5', 'Resource5') + ]; + $this->registry->shouldReceive('getResources')->andReturn($resourceSchemas); + + $requestP1 = ListResourcesRequest::make(1); + $resultP1 = $this->dispatcher->handleResourcesList($requestP1); + expect($resultP1->resources)->toHaveCount(DISPATCHER_PAGINATION_LIMIT); + expect(array_map(fn($r) => $r->name, $resultP1->resources))->toEqual(['Resource1', 'Resource2', 'Resource3']); + expect($resultP1->nextCursor)->toBe(base64_encode('offset=3')); + + // Page 2 + $requestP2 = ListResourcesRequest::make(2, $resultP1->nextCursor); + $resultP2 = $this->dispatcher->handleResourcesList($requestP2); + expect($resultP2->resources)->toHaveCount(2); + expect(array_map(fn($r) => $r->name, $resultP2->resources))->toEqual(['Resource4', 'Resource5']); + expect($resultP2->nextCursor)->toBeNull(); +}); + +it('can handle resources list request and return empty if registry has no resources', function () { + $this->registry->shouldReceive('getResources')->andReturn([]); + $request = ListResourcesRequest::make(1); + $result = $this->dispatcher->handleResourcesList($request); + expect($result->resources)->toBeEmpty(); + expect($result->nextCursor)->toBeNull(); +}); + +it('can handle resource template list request and return paginated templates', function () { + $templateSchemas = [ + ResourceTemplateSchema::make('tpl://{id}/1', 'Template1'), + ResourceTemplateSchema::make('tpl://{id}/2', 'Template2'), + ResourceTemplateSchema::make('tpl://{id}/3', 'Template3'), + ResourceTemplateSchema::make('tpl://{id}/4', 'Template4'), + ]; + $this->registry->shouldReceive('getResourceTemplates')->andReturn($templateSchemas); + + // Page 1 + $requestP1 = ListResourceTemplatesRequest::make(1); + $resultP1 = $this->dispatcher->handleResourceTemplateList($requestP1); + expect($resultP1->resourceTemplates)->toHaveCount(DISPATCHER_PAGINATION_LIMIT); + expect(array_map(fn($rt) => $rt->name, $resultP1->resourceTemplates))->toEqual(['Template1', 'Template2', 'Template3']); + expect($resultP1->nextCursor)->toBe(base64_encode('offset=3')); + + // Page 2 + $requestP2 = ListResourceTemplatesRequest::make(2, $resultP1->nextCursor); + $resultP2 = $this->dispatcher->handleResourceTemplateList($requestP2); + expect($resultP2->resourceTemplates)->toHaveCount(1); + expect(array_map(fn($rt) => $rt->name, $resultP2->resourceTemplates))->toEqual(['Template4']); + expect($resultP2->nextCursor)->toBeNull(); +}); + +it('can handle resource read request and return resource contents', function () { + $uri = 'file://data.txt'; + $resourceSchema = ResourceSchema::make($uri, 'file_resource'); + $registeredResourceMock = Mockery::mock(RegisteredResource::class, [$resourceSchema, ['MyResourceHandler', 'read'], false]); + $resourceContents = [TextContent::make('File content')]; + + $this->registry->shouldReceive('getResource')->with($uri)->andReturn($registeredResourceMock); + $registeredResourceMock->shouldReceive('read')->with($this->container, $uri)->andReturn($resourceContents); + + $request = ReadResourceRequest::make(1, $uri); + $result = $this->dispatcher->handleResourceRead($request); + + expect($result)->toBeInstanceOf(ReadResourceResult::class); + expect($result->contents)->toEqual($resourceContents); +}); + +it('can handle resource read request and throw exception if resource not found', function () { + $this->registry->shouldReceive('getResource')->with('unknown://uri')->andReturn(null); + $request = ReadResourceRequest::make(1, 'unknown://uri'); + $this->dispatcher->handleResourceRead($request); +})->throws(McpServerException::class, "Resource URI 'unknown://uri' not found."); + +it('can handle resource subscribe request and call subscription manager', function () { + $uri = 'news://updates'; + $this->session->shouldReceive('getId')->andReturn(DISPATCHER_SESSION_ID); + $this->subscriptionManager->shouldReceive('subscribe')->with(DISPATCHER_SESSION_ID, $uri)->once(); + $request = ResourceSubscribeRequest::make(1, $uri); + $result = $this->dispatcher->handleResourceSubscribe($request, $this->session); + expect($result)->toBeInstanceOf(EmptyResult::class); +}); + +it('can handle prompts list request and return paginated prompts', function () { + $promptSchemas = [ + PromptSchema::make('promptA', '', []), + PromptSchema::make('promptB', '', []), + PromptSchema::make('promptC', '', []), + PromptSchema::make('promptD', '', []), + PromptSchema::make('promptE', '', []), + PromptSchema::make('promptF', '', []), + ]; // 6 prompts + $this->registry->shouldReceive('getPrompts')->andReturn($promptSchemas); + + // Page 1 + $requestP1 = ListPromptsRequest::make(1); + $resultP1 = $this->dispatcher->handlePromptsList($requestP1); + expect($resultP1->prompts)->toHaveCount(DISPATCHER_PAGINATION_LIMIT); + expect(array_map(fn($p) => $p->name, $resultP1->prompts))->toEqual(['promptA', 'promptB', 'promptC']); + expect($resultP1->nextCursor)->toBe(base64_encode('offset=3')); + + // Page 2 + $requestP2 = ListPromptsRequest::make(2, $resultP1->nextCursor); + $resultP2 = $this->dispatcher->handlePromptsList($requestP2); + expect($resultP2->prompts)->toHaveCount(DISPATCHER_PAGINATION_LIMIT); // 3 more + expect(array_map(fn($p) => $p->name, $resultP2->prompts))->toEqual(['promptD', 'promptE', 'promptF']); + expect($resultP2->nextCursor)->toBeNull(); // End of list +}); + +it('can handle prompt get request and return prompt messages', function () { + $promptName = 'daily-summary'; + $args = ['date' => '2024-07-16']; + $promptSchema = PromptSchema::make($promptName, 'summary_prompt', [PromptArgument::make('date', required: true)]); + $registeredPromptMock = Mockery::mock(RegisteredPrompt::class, [$promptSchema, ['MyPromptHandler', 'get'], false]); + $promptMessages = [PromptMessage::make(Role::User, TextContent::make("Summary for 2024-07-16"))]; + + $this->registry->shouldReceive('getPrompt')->with($promptName)->andReturn($registeredPromptMock); + $registeredPromptMock->shouldReceive('get')->with($this->container, $args)->andReturn($promptMessages); + + $request = GetPromptRequest::make(1, $promptName, $args); + $result = $this->dispatcher->handlePromptGet($request, $this->session); + + expect($result)->toBeInstanceOf(GetPromptResult::class); + expect($result->messages)->toEqual($promptMessages); + expect($result->description)->toBe($promptSchema->description); +}); + +it('can handle prompt get request and throw exception if required argument is missing', function () { + $promptName = 'needs-topic'; + $promptSchema = PromptSchema::make($promptName, '', [PromptArgument::make('topic', required: true)]); + $registeredPromptMock = Mockery::mock(RegisteredPrompt::class, [$promptSchema, ['MyPromptHandler', 'get'], false]); + $this->registry->shouldReceive('getPrompt')->with($promptName)->andReturn($registeredPromptMock); + + $request = GetPromptRequest::make(1, $promptName, ['other_arg' => 'value']); // 'topic' is missing + $this->dispatcher->handlePromptGet($request, $this->session); +})->throws(McpServerException::class, "Missing required argument 'topic' for prompt 'needs-topic'."); + + +it('can handle logging set level request and set log level on session', function () { + $level = LoggingLevel::Debug; + $this->session->shouldReceive('getId')->andReturn(DISPATCHER_SESSION_ID); + $this->session->shouldReceive('set')->with('log_level', 'debug')->once(); + + $request = SetLogLevelRequest::make(1, $level); + $result = $this->dispatcher->handleLoggingSetLevel($request, $this->session); + + expect($result)->toBeInstanceOf(EmptyResult::class); +}); + +it('can handle completion complete request for prompt and delegate to provider', function () { + $promptName = 'my-completable-prompt'; + $argName = 'tagName'; + $currentValue = 'php'; + $completions = ['php-mcp', 'php-fig']; + $mockCompletionProvider = Mockery::mock(CompletionProviderInterface::class); + $providerClass = get_class($mockCompletionProvider); + + $promptSchema = PromptSchema::make($promptName, '', [PromptArgument::make($argName)]); + $registeredPrompt = new RegisteredPrompt( + schema: $promptSchema, + handler: ['MyPromptHandler', 'get'], + isManual: false, + completionProviders: [$argName => $providerClass] + ); + + $this->registry->shouldReceive('getPrompt')->with($promptName)->andReturn($registeredPrompt); + $this->container->shouldReceive('get')->with($providerClass)->andReturn($mockCompletionProvider); + $mockCompletionProvider->shouldReceive('getCompletions')->with($currentValue, $this->session)->andReturn($completions); + + $request = CompletionCompleteRequest::make(1, PromptReference::make($promptName), ['name' => $argName, 'value' => $currentValue]); + $result = $this->dispatcher->handleCompletionComplete($request, $this->session); + + expect($result)->toBeInstanceOf(CompletionCompleteResult::class); + expect($result->values)->toEqual($completions); + expect($result->total)->toBe(count($completions)); + expect($result->hasMore)->toBeFalse(); +}); + +it('can handle completion complete request for resource template and delegate to provider', function () { + $templateUri = 'item://{itemId}/category/{catName}'; + $uriVarName = 'catName'; + $currentValue = 'boo'; + $completions = ['books', 'boomerangs']; + $mockCompletionProvider = Mockery::mock(CompletionProviderInterface::class); + $providerClass = get_class($mockCompletionProvider); + + $templateSchema = ResourceTemplateSchema::make($templateUri, 'item-template'); + $registeredTemplate = new RegisteredResourceTemplate( + schema: $templateSchema, + handler: ['MyResourceTemplateHandler', 'get'], + isManual: false, + completionProviders: [$uriVarName => $providerClass] + ); + + $this->registry->shouldReceive('getResourceTemplate')->with($templateUri)->andReturn($registeredTemplate); + $this->container->shouldReceive('get')->with($providerClass)->andReturn($mockCompletionProvider); + $mockCompletionProvider->shouldReceive('getCompletions')->with($currentValue, $this->session)->andReturn($completions); + + $request = CompletionCompleteRequest::make(1, ResourceReference::make($templateUri), ['name' => $uriVarName, 'value' => $currentValue]); + $result = $this->dispatcher->handleCompletionComplete($request, $this->session); + + expect($result->values)->toEqual($completions); +}); + +it('can handle completion complete request and return empty if no provider', function () { + $promptName = 'no-provider-prompt'; + $promptSchema = PromptSchema::make($promptName, '', [PromptArgument::make('arg')]); + $registeredPrompt = new RegisteredPrompt( + schema: $promptSchema, + handler: ['MyPromptHandler', 'get'], + isManual: false, + completionProviders: [] + ); + $this->registry->shouldReceive('getPrompt')->with($promptName)->andReturn($registeredPrompt); + + $request = CompletionCompleteRequest::make(1, PromptReference::make($promptName), ['name' => 'arg', 'value' => '']); + $result = $this->dispatcher->handleCompletionComplete($request, $this->session); + expect($result->values)->toBeEmpty(); +}); + +it('can handle completion complete request with ListCompletionProvider instance', function () { + $promptName = 'list-completion-prompt'; + $argName = 'category'; + $currentValue = 'bl'; + $expectedCompletions = ['blog']; + + $listProvider = new \PhpMcp\Server\Defaults\ListCompletionProvider(['blog', 'news', 'docs', 'api']); + + $promptSchema = PromptSchema::make($promptName, '', [PromptArgument::make($argName)]); + $registeredPrompt = new RegisteredPrompt( + schema: $promptSchema, + handler: ['MyPromptHandler', 'get'], + isManual: false, + completionProviders: [$argName => $listProvider] + ); + + $this->registry->shouldReceive('getPrompt')->with($promptName)->andReturn($registeredPrompt); + + $request = CompletionCompleteRequest::make(1, PromptReference::make($promptName), ['name' => $argName, 'value' => $currentValue]); + $result = $this->dispatcher->handleCompletionComplete($request, $this->session); + + expect($result->values)->toEqual($expectedCompletions); + expect($result->total)->toBe(1); + expect($result->hasMore)->toBeFalse(); +}); + +it('can handle completion complete request with EnumCompletionProvider instance', function () { + $promptName = 'enum-completion-prompt'; + $argName = 'status'; + $currentValue = 'a'; + $expectedCompletions = ['archived']; + + $enumProvider = new \PhpMcp\Server\Defaults\EnumCompletionProvider(StatusEnum::class); + + $promptSchema = PromptSchema::make($promptName, '', [PromptArgument::make($argName)]); + $registeredPrompt = new RegisteredPrompt( + schema: $promptSchema, + handler: ['MyPromptHandler', 'get'], + isManual: false, + completionProviders: [$argName => $enumProvider] + ); + + $this->registry->shouldReceive('getPrompt')->with($promptName)->andReturn($registeredPrompt); + + $request = CompletionCompleteRequest::make(1, PromptReference::make($promptName), ['name' => $argName, 'value' => $currentValue]); + $result = $this->dispatcher->handleCompletionComplete($request, $this->session); + + expect($result->values)->toEqual($expectedCompletions); + expect($result->total)->toBe(1); + expect($result->hasMore)->toBeFalse(); +}); + + +it('decodeCursor handles null and invalid cursors', function () { + $method = new \ReflectionMethod(Dispatcher::class, 'decodeCursor'); + $method->setAccessible(true); + + expect($method->invoke($this->dispatcher, null))->toBe(0); + expect($method->invoke($this->dispatcher, 'not_base64_$$$'))->toBe(0); + expect($method->invoke($this->dispatcher, base64_encode('invalid_format')))->toBe(0); + expect($method->invoke($this->dispatcher, base64_encode('offset=123')))->toBe(123); +}); + +it('encodeNextCursor generates correct cursor or null', function () { + $method = new \ReflectionMethod(Dispatcher::class, 'encodeNextCursor'); + $method->setAccessible(true); + $limit = DISPATCHER_PAGINATION_LIMIT; + + expect($method->invoke($this->dispatcher, 0, $limit, 10, $limit))->toBe(base64_encode('offset=3')); + expect($method->invoke($this->dispatcher, 0, $limit, $limit, $limit))->toBeNull(); + expect($method->invoke($this->dispatcher, $limit, 2, $limit + 2 + 1, $limit))->toBe(base64_encode('offset=' . ($limit + 2))); + expect($method->invoke($this->dispatcher, $limit, 1, $limit + 1, $limit))->toBeNull(); + expect($method->invoke($this->dispatcher, 0, 0, 10, $limit))->toBeNull(); +}); diff --git a/vendor/php-mcp/server/tests/Unit/Elements/RegisteredElementTest.php b/vendor/php-mcp/server/tests/Unit/Elements/RegisteredElementTest.php new file mode 100644 index 000000000..b93c7c43f --- /dev/null +++ b/vendor/php-mcp/server/tests/Unit/Elements/RegisteredElementTest.php @@ -0,0 +1,295 @@ +container = Mockery::mock(ContainerInterface::class); + $this->container->shouldReceive('get')->with(VariousTypesHandler::class)->andReturn(new VariousTypesHandler()); +}); + +it('can be constructed as manual or discovered', function () { + $handler = [VariousTypesHandler::class, 'noArgsMethod']; + $elManual = new RegisteredElement($handler, true); + $elDiscovered = new RegisteredElement($handler, false); + expect($elManual->isManual)->toBeTrue(); + expect($elDiscovered->isManual)->toBeFalse(); + expect($elDiscovered->handler)->toBe($handler); +}); + +it('prepares arguments in correct order for simple required types', function () { + $element = new RegisteredElement([VariousTypesHandler::class, 'simpleRequiredArgs']); + $args = ['pString' => 'hello', 'pBool' => true, 'pInt' => 123]; + $result = $element->handle($this->container, $args); + + $expectedResult = ['pString' => 'hello', 'pInt' => 123, 'pBool' => true]; + + expect($result)->toBe($expectedResult); +}); + +it('uses default values for missing optional arguments', function () { + $element = new RegisteredElement([VariousTypesHandler::class, 'optionalArgsWithDefaults']); + + $result1 = $element->handle($this->container, ['pString' => 'override']); + expect($result1['pString'])->toBe('override'); + expect($result1['pInt'])->toBe(100); + expect($result1['pNullableBool'])->toBeTrue(); + expect($result1['pFloat'])->toBe(3.14); + + $result2 = $element->handle($this->container, []); + expect($result2['pString'])->toBe('default_string'); + expect($result2['pInt'])->toBe(100); + expect($result2['pNullableBool'])->toBeTrue(); + expect($result2['pFloat'])->toBe(3.14); +}); + +it('passes null for nullable arguments if not provided', function () { + $elementNoDefaults = new RegisteredElement([VariousTypesHandler::class, 'nullableArgsWithoutDefaults']); + $result2 = $elementNoDefaults->handle($this->container, []); + expect($result2['pString'])->toBeNull(); + expect($result2['pInt'])->toBeNull(); + expect($result2['pArray'])->toBeNull(); +}); + +it('passes null explicitly for nullable arguments', function () { + $element = new RegisteredElement([VariousTypesHandler::class, 'nullableArgsWithoutDefaults']); + $result = $element->handle($this->container, ['pString' => null, 'pInt' => null, 'pArray' => null]); + expect($result['pString'])->toBeNull(); + expect($result['pInt'])->toBeNull(); + expect($result['pArray'])->toBeNull(); +}); + +it('handles mixed type arguments', function () { + $element = new RegisteredElement([VariousTypesHandler::class, 'mixedTypeArg']); + $obj = new stdClass(); + $testValues = [ + 'a string', + 123, + true, + null, + ['an', 'array'], + $obj + ]; + foreach ($testValues as $value) { + $result = $element->handle($this->container, ['pMixed' => $value]); + expect($result['pMixed'])->toBe($value); + } +}); + +it('throws McpServerException for missing required argument', function () { + $element = new RegisteredElement([VariousTypesHandler::class, 'simpleRequiredArgs']); + $element->handle($this->container, ['pString' => 'hello', 'pInt' => 123]); +})->throws(McpServerException::class, 'Missing required argument `pBool`'); + +dataset('valid_type_casts', [ + 'string_from_int' => ['strParam', 123, '123'], + 'int_from_valid_string' => ['intParam', '456', 456], + 'int_from_neg_string' => ['intParam', '-10', -10], + 'int_from_float_whole' => ['intParam', 77.0, 77], + 'bool_from_int_1' => ['boolProp', 1, true], + 'bool_from_string_true' => ['boolProp', 'true', true], + 'bool_from_string_TRUE' => ['boolProp', 'TRUE', true], + 'bool_from_int_0' => ['boolProp', 0, false], + 'bool_from_string_false' => ['boolProp', 'false', false], + 'bool_from_string_FALSE' => ['boolProp', 'FALSE', false], + 'float_from_valid_string' => ['floatParam', '7.89', 7.89], + 'float_from_int' => ['floatParam', 10, 10.0], + 'array_passthrough' => ['arrayParam', ['x', 'y'], ['x', 'y']], + 'object_passthrough' => ['objectParam', (object)['a' => 1], (object)['a' => 1]], + 'string_for_int_cast_specific' => ['stringForIntCast', '999', 999], + 'string_for_float_cast_specific' => ['stringForFloatCast', '123.45', 123.45], + 'string_for_bool_true_cast_specific' => ['stringForBoolTrueCast', '1', true], + 'string_for_bool_false_cast_specific' => ['stringForBoolFalseCast', '0', false], + 'int_for_string_cast_specific' => ['intForStringCast', 55, '55'], + 'int_for_float_cast_specific' => ['intForFloatCast', 66, 66.0], + 'bool_for_string_cast_specific' => ['boolForStringCast', true, '1'], + 'backed_string_enum_valid_val' => ['backedStringEnumParam', 'A', BackedStringEnum::OptionA], + 'backed_int_enum_valid_val' => ['backedIntEnumParam', 1, BackedIntEnum::First], + 'unit_enum_valid_val' => ['unitEnumParam', 'Yes', UnitEnum::Yes], +]); + +it('casts argument types correctly for valid inputs (comprehensive)', function (string $paramName, mixed $inputValue, mixed $expectedValue) { + $element = new RegisteredElement([VariousTypesHandler::class, 'comprehensiveArgumentTest']); + + $allArgs = [ + 'strParam' => 'default string', + 'intParam' => 0, + 'boolProp' => false, + 'floatParam' => 0.0, + 'arrayParam' => [], + 'backedStringEnumParam' => BackedStringEnum::OptionA, + 'backedIntEnumParam' => BackedIntEnum::First, + 'unitEnumParam' => 'Yes', + 'nullableStringParam' => null, + 'mixedParam' => 'default mixed', + 'objectParam' => new stdClass(), + 'stringForIntCast' => '0', + 'stringForFloatCast' => '0.0', + 'stringForBoolTrueCast' => 'false', + 'stringForBoolFalseCast' => 'true', + 'intForStringCast' => 0, + 'intForFloatCast' => 0, + 'boolForStringCast' => false, + 'valueForBackedStringEnum' => 'A', + 'valueForBackedIntEnum' => 1, + ]; + $testArgs = array_merge($allArgs, [$paramName => $inputValue]); + + $result = $element->handle($this->container, $testArgs); + expect($result[$paramName])->toEqual($expectedValue); +})->with('valid_type_casts'); + + +dataset('invalid_type_casts', [ + 'int_from_alpha_string' => ['intParam', 'abc', '/Cannot cast value to integer/i'], + 'int_from_float_non_whole' => ['intParam', 12.3, '/Cannot cast value to integer/i'], + 'bool_from_string_random' => ['boolProp', 'random', '/Cannot cast value to boolean/i'], + 'bool_from_int_invalid' => ['boolProp', 2, '/Cannot cast value to boolean/i'], + 'float_from_alpha_string' => ['floatParam', 'xyz', '/Cannot cast value to float/i'], + 'array_from_string' => ['arrayParam', 'not_an_array', '/Cannot cast value to array/i'], + 'backed_string_enum_invalid_val' => ['backedStringEnumParam', 'Z', "/Invalid value 'Z' for backed enum .*BackedStringEnum/i"], + 'backed_int_enum_invalid_val' => ['backedIntEnumParam', 99, "/Invalid value '99' for backed enum .*BackedIntEnum/i"], + 'unit_enum_invalid_string_val' => ['unitEnumParam', 'Maybe', "/Invalid value 'Maybe' for unit enum .*UnitEnum/i"], +]); + +it('throws McpServerException for invalid type casting', function (string $paramName, mixed $invalidValue, string $expectedMsgRegex) { + $element = new RegisteredElement([VariousTypesHandler::class, 'comprehensiveArgumentTest']); + $allArgs = [ /* fill with defaults as in valid_type_casts */ + 'strParam' => 's', + 'intParam' => 1, + 'boolProp' => true, + 'floatParam' => 1.1, + 'arrayParam' => [], + 'backedStringEnumParam' => BackedStringEnum::OptionA, + 'backedIntEnumParam' => BackedIntEnum::First, + 'unitEnumParam' => UnitEnum::Yes, + 'nullableStringParam' => null, + 'mixedParam' => 'mix', + 'objectParam' => new stdClass(), + 'stringForIntCast' => '0', + 'stringForFloatCast' => '0.0', + 'stringForBoolTrueCast' => 'false', + 'stringForBoolFalseCast' => 'true', + 'intForStringCast' => 0, + 'intForFloatCast' => 0, + 'boolForStringCast' => false, + 'valueForBackedStringEnum' => 'A', + 'valueForBackedIntEnum' => 1, + ]; + $testArgs = array_merge($allArgs, [$paramName => $invalidValue]); + + try { + $element->handle($this->container, $testArgs); + } catch (McpServerException $e) { + expect($e->getMessage())->toMatch($expectedMsgRegex); + } +})->with('invalid_type_casts'); + +it('casts to BackedStringEnum correctly', function () { + $element = new RegisteredElement([VariousTypesHandler::class, 'backedEnumArgs']); + $result = $element->handle($this->container, ['pBackedString' => 'A', 'pBackedInt' => 1]); + expect($result['pBackedString'])->toBe(BackedStringEnum::OptionA); +}); + +it('throws for invalid BackedStringEnum value', function () { + $element = new RegisteredElement([VariousTypesHandler::class, 'backedEnumArgs']); + $element->handle($this->container, ['pBackedString' => 'Invalid', 'pBackedInt' => 1]); +})->throws(McpServerException::class, "Invalid value 'Invalid' for backed enum"); + +it('casts to BackedIntEnum correctly', function () { + $element = new RegisteredElement([VariousTypesHandler::class, 'backedEnumArgs']); + $result = $element->handle($this->container, ['pBackedString' => 'A', 'pBackedInt' => 2]); + expect($result['pBackedInt'])->toBe(BackedIntEnum::Second); +}); + +it('throws for invalid BackedIntEnum value', function () { + $element = new RegisteredElement([VariousTypesHandler::class, 'backedEnumArgs']); + $element->handle($this->container, ['pBackedString' => 'A', 'pBackedInt' => 999]); +})->throws(McpServerException::class, "Invalid value '999' for backed enum"); + +it('casts to UnitEnum correctly', function () { + $element = new RegisteredElement([VariousTypesHandler::class, 'unitEnumArg']); + $result = $element->handle($this->container, ['pUnitEnum' => 'Yes']); + expect($result['pUnitEnum'])->toBe(UnitEnum::Yes); +}); + +it('throws for invalid UnitEnum value', function () { + $element = new RegisteredElement([VariousTypesHandler::class, 'unitEnumArg']); + $element->handle($this->container, ['pUnitEnum' => 'Invalid']); +})->throws(McpServerException::class, "Invalid value 'Invalid' for unit enum"); + + +it('throws ReflectionException if handler method does not exist', function () { + $element = new RegisteredElement([VariousTypesHandler::class, 'nonExistentMethod']); + $element->handle($this->container, []); +})->throws(\ReflectionException::class, "VariousTypesHandler::nonExistentMethod() does not exist"); + + +describe('Handler Types', function () { + it('handles invokable class handler', function () { + $this->container->shouldReceive('get') + ->with(MyInvokableTestHandler::class) + ->andReturn(new MyInvokableTestHandler()); + + $element = new RegisteredElement(MyInvokableTestHandler::class); + $result = $element->handle($this->container, ['name' => 'World']); + + expect($result)->toBe('Hello, World!'); + }); + + it('handles closure handler', function () { + $closure = function (string $a, string $b) { + return $a . $b; + }; + $element = new RegisteredElement($closure); + $result = $element->handle($this->container, ['a' => 'foo', 'b' => 'bar']); + expect($result)->toBe('foobar'); + }); + + it('handles static method handler', function () { + $handler = [MyStaticMethodTestHandler::class, 'myStaticMethod']; + $element = new RegisteredElement($handler); + $result = $element->handle($this->container, ['a' => 5, 'b' => 10]); + expect($result)->toBe(15); + }); + + it('handles global function name handler', function () { + $handler = 'PhpMcp\Server\Tests\Unit\Elements\my_global_test_function'; + $element = new RegisteredElement($handler); + $result = $element->handle($this->container, ['flag' => true]); + expect($result)->toBe('on'); + }); +}); diff --git a/vendor/php-mcp/server/tests/Unit/Elements/RegisteredPromptTest.php b/vendor/php-mcp/server/tests/Unit/Elements/RegisteredPromptTest.php new file mode 100644 index 000000000..e3ff63788 --- /dev/null +++ b/vendor/php-mcp/server/tests/Unit/Elements/RegisteredPromptTest.php @@ -0,0 +1,280 @@ +container = Mockery::mock(ContainerInterface::class); + $this->container->shouldReceive('get') + ->with(PromptHandlerFixture::class) + ->andReturn(new PromptHandlerFixture()) + ->byDefault(); + + $this->promptSchema = PromptSchema::make( + 'test-greeting-prompt', + 'Generates a greeting.', + [PromptArgument::make('name', 'The name to greet.', true)] + ); +}); + +it('constructs correctly with schema, handler, and completion providers', function () { + $providers = ['name' => CompletionProviderFixture::class]; + $prompt = RegisteredPrompt::make( + $this->promptSchema, + [PromptHandlerFixture::class, 'promptWithArgumentCompletion'], + false, + $providers + ); + + expect($prompt->schema)->toBe($this->promptSchema); + expect($prompt->handler)->toBe([PromptHandlerFixture::class, 'promptWithArgumentCompletion']); + expect($prompt->isManual)->toBeFalse(); + expect($prompt->completionProviders)->toEqual($providers); + expect($prompt->completionProviders['name'])->toBe(CompletionProviderFixture::class); + expect($prompt->completionProviders)->not->toHaveKey('nonExistentArg'); +}); + +it('can be made as a manual registration', function () { + $manualPrompt = RegisteredPrompt::make($this->promptSchema, [PromptHandlerFixture::class, 'generateSimpleGreeting'], true); + expect($manualPrompt->isManual)->toBeTrue(); +}); + +it('calls handler with prepared arguments via get()', function () { + $handlerMock = Mockery::mock(PromptHandlerFixture::class); + $handlerMock->shouldReceive('generateSimpleGreeting') + ->with('Alice', 'warm') + ->once() + ->andReturn([['role' => 'user', 'content' => 'Warm greeting for Alice.']]); + $this->container->shouldReceive('get')->with(PromptHandlerFixture::class)->andReturn($handlerMock); + + $prompt = RegisteredPrompt::make($this->promptSchema, [PromptHandlerFixture::class, 'generateSimpleGreeting']); + $messages = $prompt->get($this->container, ['name' => 'Alice', 'style' => 'warm']); + + expect($messages[0]->content->text)->toBe('Warm greeting for Alice.'); +}); + +it('formats single PromptMessage object from handler', function () { + $prompt = RegisteredPrompt::make($this->promptSchema, [PromptHandlerFixture::class, 'returnSinglePromptMessageObject']); + $messages = $prompt->get($this->container, []); + expect($messages)->toBeArray()->toHaveCount(1); + expect($messages[0])->toBeInstanceOf(PromptMessage::class); + expect($messages[0]->content->text)->toBe("Single PromptMessage object."); +}); + +it('formats array of PromptMessage objects from handler as is', function () { + $prompt = RegisteredPrompt::make($this->promptSchema, [PromptHandlerFixture::class, 'returnArrayOfPromptMessageObjects']); + $messages = $prompt->get($this->container, []); + expect($messages)->toBeArray()->toHaveCount(2); + expect($messages[0]->content->text)->toBe("First message object."); + expect($messages[1]->content)->toBeInstanceOf(ImageContent::class); +}); + +it('formats empty array from handler as empty array', function () { + $prompt = RegisteredPrompt::make($this->promptSchema, [PromptHandlerFixture::class, 'returnEmptyArrayForPrompt']); + $messages = $prompt->get($this->container, []); + expect($messages)->toBeArray()->toBeEmpty(); +}); + +it('formats simple user/assistant map from handler', function () { + $prompt = RegisteredPrompt::make($this->promptSchema, [PromptHandlerFixture::class, 'returnSimpleUserAssistantMap']); + $messages = $prompt->get($this->container, []); + expect($messages)->toHaveCount(2); + expect($messages[0]->role)->toBe(Role::User); + expect($messages[0]->content->text)->toBe("This is the user's turn."); + expect($messages[1]->role)->toBe(Role::Assistant); + expect($messages[1]->content->text)->toBe("And this is the assistant's reply."); +}); + +it('formats user/assistant map with Content objects', function () { + $prompt = RegisteredPrompt::make($this->promptSchema, [PromptHandlerFixture::class, 'returnUserAssistantMapWithContentObjects']); + $messages = $prompt->get($this->container, []); + expect($messages[0]->role)->toBe(Role::User); + expect($messages[0]->content)->toBeInstanceOf(TextContent::class)->text->toBe("User text content object."); + expect($messages[1]->role)->toBe(Role::Assistant); + expect($messages[1]->content)->toBeInstanceOf(ImageContent::class)->data->toBe("asst_img_data"); +}); + +it('formats user/assistant map with mixed content (string and Content object)', function () { + $prompt = RegisteredPrompt::make($this->promptSchema, [PromptHandlerFixture::class, 'returnUserAssistantMapWithMixedContent']); + $messages = $prompt->get($this->container, []); + expect($messages[0]->role)->toBe(Role::User); + expect($messages[0]->content)->toBeInstanceOf(TextContent::class)->text->toBe("Plain user string."); + expect($messages[1]->role)->toBe(Role::Assistant); + expect($messages[1]->content)->toBeInstanceOf(AudioContent::class)->data->toBe("aud_data"); +}); + +it('formats user/assistant map with array content', function () { + $prompt = RegisteredPrompt::make($this->promptSchema, [PromptHandlerFixture::class, 'returnUserAssistantMapWithArrayContent']); + $messages = $prompt->get($this->container, []); + expect($messages[0]->role)->toBe(Role::User); + expect($messages[0]->content)->toBeInstanceOf(TextContent::class)->text->toBe("User array content"); + expect($messages[1]->role)->toBe(Role::Assistant); + expect($messages[1]->content)->toBeInstanceOf(ImageContent::class)->data->toBe("asst_arr_img_data"); +}); + +it('formats list of raw message arrays with various content types', function () { + $prompt = RegisteredPrompt::make($this->promptSchema, [PromptHandlerFixture::class, 'returnListOfRawMessageArrays']); + $messages = $prompt->get($this->container, []); + expect($messages)->toHaveCount(6); + expect($messages[0]->content->text)->toBe("First raw message string."); + expect($messages[1]->content)->toBeInstanceOf(TextContent::class); + expect($messages[2]->content)->toBeInstanceOf(ImageContent::class)->data->toBe("raw_img_data"); + expect($messages[3]->content)->toBeInstanceOf(AudioContent::class)->data->toBe("raw_aud_data"); + expect($messages[4]->content)->toBeInstanceOf(EmbeddedResource::class); + expect($messages[4]->content->resource->blob)->toBe(base64_encode('pdf-data')); + expect($messages[5]->content)->toBeInstanceOf(EmbeddedResource::class); + expect($messages[5]->content->resource->text)->toBe('{"theme":"dark"}'); +}); + +it('formats list of raw message arrays with scalar or array content (becoming JSON TextContent)', function () { + $prompt = RegisteredPrompt::make($this->promptSchema, [PromptHandlerFixture::class, 'returnListOfRawMessageArraysWithScalars']); + $messages = $prompt->get($this->container, []); + expect($messages)->toHaveCount(5); + expect($messages[0]->content->text)->toBe("123"); + expect($messages[1]->content->text)->toBe("true"); + expect($messages[2]->content->text)->toBe("(null)"); + expect($messages[3]->content->text)->toBe("3.14"); + expect($messages[4]->content->text)->toBe(json_encode(['key' => 'value'], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); +}); + +it('formats mixed array of PromptMessage objects and raw message arrays', function () { + $prompt = RegisteredPrompt::make($this->promptSchema, [PromptHandlerFixture::class, 'returnMixedArrayOfPromptMessagesAndRaw']); + $messages = $prompt->get($this->container, []); + expect($messages)->toHaveCount(4); + expect($messages[0]->content)->toBeInstanceOf(TextContent::class)->text->toBe("This is a PromptMessage object."); + expect($messages[1]->content)->toBeInstanceOf(TextContent::class)->text->toBe("This is a raw message array."); + expect($messages[2]->content)->toBeInstanceOf(ImageContent::class)->data->toBe("pm_img"); + expect($messages[3]->content)->toBeInstanceOf(TextContent::class)->text->toBe("Raw message with typed content."); +}); + + +dataset('prompt_format_errors', [ + 'non_array_return' => ['promptReturnsNonArray', '/Prompt generator method must return an array/'], + 'invalid_role_in_array' => ['promptReturnsInvalidRole', "/Invalid role 'system'/"], + 'invalid_content_structure_in_array' => ['promptReturnsArrayWithInvalidContentStructure', "/Invalid message format at index 0. Expected an array with 'role' and 'content' keys./"], // More specific from formatMessage + 'invalid_typed_content_in_array' => ['promptReturnsArrayWithInvalidTypedContent', "/Invalid 'image' content at index 0: Missing or invalid 'data' string/"], + 'invalid_resource_content_in_array' => ['promptReturnsArrayWithInvalidResourceContent', "/Invalid resource at index 0: Must contain 'text' or 'blob'./"], +]); + +it('throws RuntimeException for invalid prompt result formats', function (string|callable $handlerMethodOrCallable, string $expectedErrorPattern) { + $methodName = is_string($handlerMethodOrCallable) ? $handlerMethodOrCallable : 'customReturn'; + $prompt = RegisteredPrompt::make($this->promptSchema, [PromptHandlerFixture::class, $methodName]); + + if (is_callable($handlerMethodOrCallable)) { + $this->container->shouldReceive('get')->with(PromptHandlerFixture::class)->andReturn( + Mockery::mock(PromptHandlerFixture::class, [$methodName => $handlerMethodOrCallable()]) + ); + } + + try { + $prompt->get($this->container, []); + } catch (\RuntimeException $e) { + expect($e->getMessage())->toMatch($expectedErrorPattern); + } + + expect($prompt->toArray())->toBeArray(); +})->with('prompt_format_errors'); + + +it('propagates exceptions from handler during get()', function () { + $prompt = RegisteredPrompt::make($this->promptSchema, [PromptHandlerFixture::class, 'promptHandlerThrows']); + $prompt->get($this->container, []); +})->throws(\LogicException::class, "Prompt generation failed inside handler."); + + +it('can be serialized to array and deserialized with completion providers', function () { + $schema = PromptSchema::make( + 'serialize-prompt', + 'Test SerDe', + [PromptArgument::make('arg1', required: true), PromptArgument::make('arg2', 'description for arg2')] + ); + $providers = ['arg1' => CompletionProviderFixture::class]; + $serializedProviders = ['arg1' => serialize(CompletionProviderFixture::class)]; + $original = RegisteredPrompt::make( + $schema, + [PromptHandlerFixture::class, 'generateSimpleGreeting'], + true, + $providers + ); + + $array = $original->toArray(); + + expect($array['schema']['name'])->toBe('serialize-prompt'); + expect($array['schema']['arguments'])->toHaveCount(2); + expect($array['handler'])->toBe([PromptHandlerFixture::class, 'generateSimpleGreeting']); + expect($array['isManual'])->toBeTrue(); + expect($array['completionProviders'])->toEqual($serializedProviders); + + $rehydrated = RegisteredPrompt::fromArray($array); + expect($rehydrated)->toBeInstanceOf(RegisteredPrompt::class); + expect($rehydrated->schema->name)->toEqual($original->schema->name); + expect($rehydrated->isManual)->toBeTrue(); + expect($rehydrated->completionProviders)->toEqual($providers); +}); + +it('fromArray returns false on failure for prompt', function () { + $badData = ['schema' => ['name' => 'fail']]; + expect(RegisteredPrompt::fromArray($badData))->toBeFalse(); +}); + +it('can be serialized with ListCompletionProvider instances', function () { + $schema = PromptSchema::make( + 'list-prompt', + 'Test list completion', + [PromptArgument::make('status')] + ); + $listProvider = new \PhpMcp\Server\Defaults\ListCompletionProvider(['draft', 'published', 'archived']); + $providers = ['status' => $listProvider]; + + $original = RegisteredPrompt::make( + $schema, + [PromptHandlerFixture::class, 'generateSimpleGreeting'], + true, + $providers + ); + + $array = $original->toArray(); + expect($array['completionProviders']['status'])->toBeString(); // Serialized instance + + $rehydrated = RegisteredPrompt::fromArray($array); + expect($rehydrated->completionProviders['status'])->toBeInstanceOf(\PhpMcp\Server\Defaults\ListCompletionProvider::class); +}); + +it('can be serialized with EnumCompletionProvider instances', function () { + $schema = PromptSchema::make( + 'enum-prompt', + 'Test enum completion', + [PromptArgument::make('priority')] + ); + + $enumProvider = new \PhpMcp\Server\Defaults\EnumCompletionProvider(StatusEnum::class); + $providers = ['priority' => $enumProvider]; + + $original = RegisteredPrompt::make( + $schema, + [PromptHandlerFixture::class, 'generateSimpleGreeting'], + true, + $providers + ); + + $array = $original->toArray(); + expect($array['completionProviders']['priority'])->toBeString(); // Serialized instance + + $rehydrated = RegisteredPrompt::fromArray($array); + expect($rehydrated->completionProviders['priority'])->toBeInstanceOf(\PhpMcp\Server\Defaults\EnumCompletionProvider::class); +}); diff --git a/vendor/php-mcp/server/tests/Unit/Elements/RegisteredResourceTemplateTest.php b/vendor/php-mcp/server/tests/Unit/Elements/RegisteredResourceTemplateTest.php new file mode 100644 index 000000000..ce3dd5e11 --- /dev/null +++ b/vendor/php-mcp/server/tests/Unit/Elements/RegisteredResourceTemplateTest.php @@ -0,0 +1,215 @@ +container = Mockery::mock(ContainerInterface::class); + $this->handlerInstance = new ResourceHandlerFixture(); + $this->container->shouldReceive('get') + ->with(ResourceHandlerFixture::class) + ->andReturn($this->handlerInstance) + ->byDefault(); + + $this->templateUri = 'item://{category}/{itemId}/details'; + $this->resourceTemplateSchema = ResourceTemplate::make( + $this->templateUri, + 'item-details-template', + mimeType: 'application/json' + ); + + $this->defaultHandlerMethod = 'getUserDocument'; + $this->matchingTemplateSchema = ResourceTemplate::make( + 'user://{userId}/doc/{documentId}', + 'user-doc-template', + mimeType: 'application/json' + ); +}); + +it('constructs correctly with schema, handler, and completion providers', function () { + $completionProviders = [ + 'userId' => CompletionProviderFixture::class, + 'documentId' => 'Another\ProviderClass' + ]; + + $schema = ResourceTemplate::make( + 'user://{userId}/doc/{documentId}', + 'user-doc-template', + mimeType: 'application/json' + ); + + $template = RegisteredResourceTemplate::make( + schema: $schema, + handler: [ResourceHandlerFixture::class, 'getUserDocument'], + completionProviders: $completionProviders + ); + + expect($template->schema)->toBe($schema); + expect($template->handler)->toBe([ResourceHandlerFixture::class, 'getUserDocument']); + expect($template->isManual)->toBeFalse(); + expect($template->completionProviders)->toEqual($completionProviders); + expect($template->completionProviders['userId'])->toBe(CompletionProviderFixture::class); + expect($template->completionProviders['documentId'])->toBe('Another\ProviderClass'); + expect($template->completionProviders)->not->toHaveKey('nonExistentVar'); +}); + +it('can be made as a manual registration', function () { + $schema = ResourceTemplate::make( + 'user://{userId}/doc/{documentId}', + 'user-doc-template', + mimeType: 'application/json' + ); + + $manualTemplate = RegisteredResourceTemplate::make( + schema: $schema, + handler: [ResourceHandlerFixture::class, 'getUserDocument'], + isManual: true + ); + + expect($manualTemplate->isManual)->toBeTrue(); +}); + +dataset('uri_template_matching_cases', [ + 'simple_var' => ['user://{userId}', 'user://12345', ['userId' => '12345']], + 'simple_var_alpha' => ['user://{userId}', 'user://abc-def', ['userId' => 'abc-def']], + 'no_match_missing_var_part' => ['user://{userId}', 'user://', null], + 'no_match_prefix' => ['user://{userId}', 'users://12345', null], + 'multi_var' => ['item://{category}/{itemId}/details', 'item://books/978-abc/details', ['category' => 'books', 'itemId' => '978-abc']], + 'multi_var_empty_segment_fail' => ['item://{category}/{itemId}/details', 'item://books//details', null], // [^/]+ fails on empty segment + 'multi_var_wrong_literal_end' => ['item://{category}/{itemId}/details', 'item://books/978-abc/summary', null], + 'multi_var_no_suffix_literal' => ['item://{category}/{itemId}', 'item://tools/hammer', ['category' => 'tools', 'itemId' => 'hammer']], + 'multi_var_extra_segment_fail' => ['item://{category}/{itemId}', 'item://tools/hammer/extra', null], + 'mixed_literals_vars' => ['user://{userId}/profile/pic_{picId}.jpg', 'user://kp/profile/pic_main.jpg', ['userId' => 'kp', 'picId' => 'main']], + 'mixed_wrong_extension' => ['user://{userId}/profile/pic_{picId}.jpg', 'user://kp/profile/pic_main.png', null], + 'mixed_wrong_literal_prefix' => ['user://{userId}/profile/img_{picId}.jpg', 'user://kp/profile/pic_main.jpg', null], + 'escapable_chars_in_literal' => ['search://{query}/results.json?page={pageNo}', 'search://term.with.dots/results.json?page=2', ['query' => 'term.with.dots', 'pageNo' => '2']], +]); + +it('matches URIs against template and extracts variables correctly', function (string $templateString, string $uriToTest, ?array $expectedVariables) { + $schema = ResourceTemplate::make($templateString, 'test-match'); + $template = RegisteredResourceTemplate::make($schema, [ResourceHandlerFixture::class, 'getUserDocument']); + + if ($expectedVariables !== null) { + expect($template->matches($uriToTest))->toBeTrue(); + $reflection = new \ReflectionClass($template); + $prop = $reflection->getProperty('uriVariables'); + $prop->setAccessible(true); + expect($prop->getValue($template))->toEqual($expectedVariables); + } else { + expect($template->matches($uriToTest))->toBeFalse(); + } +})->with('uri_template_matching_cases'); + +it('gets variable names from compiled template', function () { + $schema = ResourceTemplate::make('foo://{varA}/bar/{varB_ext}.{format}', 'vars-test'); + $template = RegisteredResourceTemplate::make($schema, [ResourceHandlerFixture::class, 'getUserDocument']); + expect($template->getVariableNames())->toEqualCanonicalizing(['varA', 'varB_ext', 'format']); +}); + +it('reads resource using handler with extracted URI variables', function () { + $uriTemplate = 'item://{category}/{itemId}?format={format}'; + $uri = 'item://electronics/tv-123?format=json_pretty'; + $schema = ResourceTemplate::make($uriTemplate, 'item-details-template'); + $template = RegisteredResourceTemplate::make($schema, [ResourceHandlerFixture::class, 'getTemplatedContent']); + + expect($template->matches($uri))->toBeTrue(); + + $resultContents = $template->read($this->container, $uri); + + expect($resultContents)->toBeArray()->toHaveCount(1); + + $content = $resultContents[0]; + expect($content)->toBeInstanceOf(TextResourceContents::class); + expect($content->uri)->toBe($uri); + expect($content->mimeType)->toBe('application/json'); + + $decodedText = json_decode($content->text, true); + expect($decodedText['message'])->toBe("Content for item tv-123 in category electronics, format json_pretty."); + expect($decodedText['category_received'])->toBe('electronics'); + expect($decodedText['itemId_received'])->toBe('tv-123'); + expect($decodedText['format_received'])->toBe('json_pretty'); +}); + +it('uses mimeType from schema if handler result does not specify one', function () { + $uriTemplate = 'item://{category}/{itemId}?format={format}'; + $uri = 'item://books/bestseller?format=json_pretty'; + $schema = ResourceTemplate::make($uriTemplate, 'test-mime', mimeType: 'application/vnd.custom-template-xml'); + $template = RegisteredResourceTemplate::make($schema, [ResourceHandlerFixture::class, 'getTemplatedContent']); + expect($template->matches($uri))->toBeTrue(); + + $resultContents = $template->read($this->container, $uri); + expect($resultContents[0]->mimeType)->toBe('application/vnd.custom-template-xml'); +}); + +it('formats a simple string result from handler correctly for template', function () { + $uri = 'item://tools/hammer'; + $schema = ResourceTemplate::make('item://{type}/{name}', 'test-simple-string', mimeType: 'text/x-custom'); + $template = RegisteredResourceTemplate::make($schema, [ResourceHandlerFixture::class, 'returnStringText']); + expect($template->matches($uri))->toBeTrue(); + + $mockHandler = Mockery::mock(ResourceHandlerFixture::class); + $mockHandler->shouldReceive('returnStringText')->with($uri)->once()->andReturn('Simple content from template handler'); + $this->container->shouldReceive('get')->with(ResourceHandlerFixture::class)->andReturn($mockHandler); + + $resultContents = $template->read($this->container, $uri); + expect($resultContents[0])->toBeInstanceOf(TextResourceContents::class) + ->and($resultContents[0]->text)->toBe('Simple content from template handler') + ->and($resultContents[0]->mimeType)->toBe('text/x-custom'); // From schema +}); + +it('propagates exceptions from handler during read', function () { + $uri = 'item://tools/hammer'; + $schema = ResourceTemplate::make('item://{type}/{name}', 'test-simple-string', mimeType: 'text/x-custom'); + $template = RegisteredResourceTemplate::make($schema, [ResourceHandlerFixture::class, 'handlerThrowsException']); + expect($template->matches($uri))->toBeTrue(); + $template->read($this->container, $uri); +})->throws(\DomainException::class, "Cannot read resource"); + +it('can be serialized to array and deserialized', function () { + $schema = ResourceTemplate::make( + 'obj://{type}/{id}', + 'my-template', + mimeType: 'application/template+json', + annotations: Annotations::make(priority: 0.7) + ); + + $providers = ['type' => CompletionProviderFixture::class]; + $serializedProviders = ['type' => serialize(CompletionProviderFixture::class)]; + + $original = RegisteredResourceTemplate::make( + $schema, + [ResourceHandlerFixture::class, 'getUserDocument'], + true, + $providers + ); + + $array = $original->toArray(); + + expect($array['schema']['uriTemplate'])->toBe('obj://{type}/{id}'); + expect($array['schema']['name'])->toBe('my-template'); + expect($array['schema']['mimeType'])->toBe('application/template+json'); + expect($array['schema']['annotations']['priority'])->toBe(0.7); + expect($array['handler'])->toBe([ResourceHandlerFixture::class, 'getUserDocument']); + expect($array['isManual'])->toBeTrue(); + expect($array['completionProviders'])->toEqual($serializedProviders); + + $rehydrated = RegisteredResourceTemplate::fromArray($array); + expect($rehydrated)->toBeInstanceOf(RegisteredResourceTemplate::class); + expect($rehydrated->schema->uriTemplate)->toEqual($original->schema->uriTemplate); + expect($rehydrated->schema->name)->toEqual($original->schema->name); + expect($rehydrated->isManual)->toBeTrue(); + expect($rehydrated->completionProviders)->toEqual($providers); +}); + +it('fromArray returns false on failure', function () { + $badData = ['schema' => ['uriTemplate' => 'fail']]; + expect(RegisteredResourceTemplate::fromArray($badData))->toBeFalse(); +}); diff --git a/vendor/php-mcp/server/tests/Unit/Elements/RegisteredResourceTest.php b/vendor/php-mcp/server/tests/Unit/Elements/RegisteredResourceTest.php new file mode 100644 index 000000000..85976c5b3 --- /dev/null +++ b/vendor/php-mcp/server/tests/Unit/Elements/RegisteredResourceTest.php @@ -0,0 +1,223 @@ +container = Mockery::mock(ContainerInterface::class); + $this->handlerInstance = new ResourceHandlerFixture(); + $this->container->shouldReceive('get') + ->with(ResourceHandlerFixture::class) + ->andReturn($this->handlerInstance) + ->byDefault(); + + $this->testUri = 'test://resource/item.txt'; + $this->resourceSchema = ResourceSchema::make($this->testUri, 'test-resource', mimeType: 'text/plain'); + $this->registeredResource = RegisteredResource::make( + $this->resourceSchema, + [ResourceHandlerFixture::class, 'returnStringText'] + ); +}); + +afterEach(function () { + if (ResourceHandlerFixture::$unlinkableSplFile && file_exists(ResourceHandlerFixture::$unlinkableSplFile)) { + @unlink(ResourceHandlerFixture::$unlinkableSplFile); + ResourceHandlerFixture::$unlinkableSplFile = null; + } +}); + +it('constructs correctly and exposes schema', function () { + expect($this->registeredResource->schema)->toBe($this->resourceSchema); + expect($this->registeredResource->handler)->toBe([ResourceHandlerFixture::class, 'returnStringText']); + expect($this->registeredResource->isManual)->toBeFalse(); +}); + +it('can be made as a manual registration', function () { + $manualResource = RegisteredResource::make($this->resourceSchema, [ResourceHandlerFixture::class, 'returnStringText'], true); + expect($manualResource->isManual)->toBeTrue(); +}); + +it('passes URI to handler if handler method expects it', function () { + $resource = RegisteredResource::make( + ResourceSchema::make($this->testUri, 'needs-uri'), + [ResourceHandlerFixture::class, 'resourceHandlerNeedsUri'] + ); + + $handlerMock = Mockery::mock(ResourceHandlerFixture::class); + $handlerMock->shouldReceive('resourceHandlerNeedsUri') + ->with($this->testUri) + ->once() + ->andReturn("Confirmed URI: {$this->testUri}"); + $this->container->shouldReceive('get')->with(ResourceHandlerFixture::class)->andReturn($handlerMock); + + $result = $resource->read($this->container, $this->testUri); + expect($result[0]->text)->toBe("Confirmed URI: {$this->testUri}"); +}); + +it('does not require handler method to accept URI', function () { + $resource = RegisteredResource::make( + ResourceSchema::make($this->testUri, 'no-uri-param'), + [ResourceHandlerFixture::class, 'resourceHandlerDoesNotNeedUri'] + ); + $handlerMock = Mockery::mock(ResourceHandlerFixture::class); + $handlerMock->shouldReceive('resourceHandlerDoesNotNeedUri')->once()->andReturn("Success no URI"); + $this->container->shouldReceive('get')->with(ResourceHandlerFixture::class)->andReturn($handlerMock); + + $result = $resource->read($this->container, $this->testUri); + expect($result[0]->text)->toBe("Success no URI"); +}); + + +dataset('resource_handler_return_types', [ + 'string_text' => ['returnStringText', 'text/plain', fn($text, $uri) => expect($text)->toBe("Plain string content for {$uri}"), null], + 'string_json_guess' => ['returnStringJson', 'application/json', fn($text, $uri) => expect(json_decode($text, true)['uri_in_json'])->toBe($uri), null], + 'string_html_guess' => ['returnStringHtml', 'text/html', fn($text, $uri) => expect($text)->toContain("{$uri}"), null], + 'array_json_schema_mime' => ['returnArrayJson', 'application/json', fn($text, $uri) => expect(json_decode($text, true)['uri_in_array'])->toBe($uri), null], // schema has text/plain, overridden by array + JSON content + 'empty_array' => ['returnEmptyArray', 'application/json', fn($text) => expect($text)->toBe('[]'), null], + 'stream_octet' => ['returnStream', 'application/octet-stream', null, fn($blob, $uri) => expect(base64_decode($blob ?? ''))->toBe("Streamed content for {$uri}")], + 'array_for_blob' => ['returnArrayForBlobSchema', 'application/x-custom-blob-array', null, fn($blob, $uri) => expect(base64_decode($blob ?? ''))->toBe("Blob for {$uri} via array")], + 'array_for_text' => ['returnArrayForTextSchema', 'text/vnd.custom-array-text', fn($text, $uri) => expect($text)->toBe("Text from array for {$uri} via array"), null], + 'direct_TextResourceContents' => ['returnTextResourceContents', 'text/special-contents', fn($text) => expect($text)->toBe('Direct TextResourceContents'), null], + 'direct_BlobResourceContents' => ['returnBlobResourceContents', 'application/custom-blob-contents', null, fn($blob) => expect(base64_decode($blob ?? ''))->toBe('blobbycontents')], + 'direct_EmbeddedResource' => ['returnEmbeddedResource', 'application/vnd.custom-embedded', fn($text) => expect($text)->toBe('Direct EmbeddedResource content'), null], +]); + +it('formats various handler return types correctly', function (string $handlerMethod, string $expectedMime, ?callable $textAssertion, ?callable $blobAssertion) { + $schema = ResourceSchema::make($this->testUri, 'format-test'); + $resource = RegisteredResource::make($schema, [ResourceHandlerFixture::class, $handlerMethod]); + + $resultContents = $resource->read($this->container, $this->testUri); + + expect($resultContents)->toBeArray()->toHaveCount(1); + $content = $resultContents[0]; + + expect($content->uri)->toBe($this->testUri); + expect($content->mimeType)->toBe($expectedMime); + + if ($textAssertion) { + expect($content)->toBeInstanceOf(TextResourceContents::class); + $textAssertion($content->text, $this->testUri); + } + if ($blobAssertion) { + expect($content)->toBeInstanceOf(BlobResourceContents::class); + $blobAssertion($content->blob, $this->testUri); + } +})->with('resource_handler_return_types'); + +it('formats SplFileInfo based on schema MIME type (text)', function () { + $schema = ResourceSchema::make($this->testUri, 'spl-text', mimeType: 'text/markdown'); + $resource = RegisteredResource::make($schema, [ResourceHandlerFixture::class, 'returnSplFileInfo']); + $result = $resource->read($this->container, $this->testUri); + + expect($result[0])->toBeInstanceOf(TextResourceContents::class); + expect($result[0]->mimeType)->toBe('text/markdown'); + expect($result[0]->text)->toBe("Content from SplFileInfo for {$this->testUri}"); +}); + +it('formats SplFileInfo based on schema MIME type (blob if not text like)', function () { + $schema = ResourceSchema::make($this->testUri, 'spl-blob', mimeType: 'image/png'); + $resource = RegisteredResource::make($schema, [ResourceHandlerFixture::class, 'returnSplFileInfo']); + $result = $resource->read($this->container, $this->testUri); + + expect($result[0])->toBeInstanceOf(BlobResourceContents::class); + expect($result[0]->mimeType)->toBe('image/png'); + expect(base64_decode($result[0]->blob ?? ''))->toBe("Content from SplFileInfo for {$this->testUri}"); +}); + +it('formats array of ResourceContents as is', function () { + $resource = RegisteredResource::make($this->resourceSchema, [ResourceHandlerFixture::class, 'returnArrayOfResourceContents']); + $results = $resource->read($this->container, $this->testUri); + expect($results)->toHaveCount(2); + expect($results[0])->toBeInstanceOf(TextResourceContents::class)->text->toBe('Part 1 of many RC'); + expect($results[1])->toBeInstanceOf(BlobResourceContents::class)->blob->toBe(base64_encode('pngdata')); +}); + +it('formats array of EmbeddedResources by extracting their inner resource', function () { + $resource = RegisteredResource::make($this->resourceSchema, [ResourceHandlerFixture::class, 'returnArrayOfEmbeddedResources']); + $results = $resource->read($this->container, $this->testUri); + expect($results)->toHaveCount(2); + expect($results[0])->toBeInstanceOf(TextResourceContents::class)->text->toBe(''); + expect($results[1])->toBeInstanceOf(BlobResourceContents::class)->blob->toBe(base64_encode('fontdata')); +}); + +it('formats mixed array with ResourceContent/EmbeddedResource by processing each item', function () { + $resource = RegisteredResource::make($this->resourceSchema, [ResourceHandlerFixture::class, 'returnMixedArrayWithResourceTypes']); + $results = $resource->read($this->container, $this->testUri); + + expect($results)->toBeArray()->toHaveCount(4); + expect($results[0])->toBeInstanceOf(TextResourceContents::class)->text->toBe("A raw string piece"); + expect($results[1])->toBeInstanceOf(TextResourceContents::class)->text->toBe("**Markdown!**"); + expect($results[2])->toBeInstanceOf(TextResourceContents::class); + expect(json_decode($results[2]->text, true))->toEqual(['nested_array_data' => 'value', 'for_uri' => $this->testUri]); + expect($results[3])->toBeInstanceOf(TextResourceContents::class)->text->toBe("col1,col2"); +}); + + +it('propagates McpServerException from handler during read', function () { + $resource = RegisteredResource::make( + $this->resourceSchema, + [ResourceHandlerFixture::class, 'resourceHandlerNeedsUri'] + ); + $this->container->shouldReceive('get')->with(ResourceHandlerFixture::class)->andReturn( + Mockery::mock(ResourceHandlerFixture::class, function (Mockery\MockInterface $mock) { + $mock->shouldReceive('resourceHandlerNeedsUri')->andThrow(McpServerException::invalidParams("Test error")); + }) + ); + $resource->read($this->container, $this->testUri); +})->throws(McpServerException::class, "Test error"); + +it('propagates other exceptions from handler during read', function () { + $resource = RegisteredResource::make($this->resourceSchema, [ResourceHandlerFixture::class, 'handlerThrowsException']); + $resource->read($this->container, $this->testUri); +})->throws(\DomainException::class, "Cannot read resource"); + +it('throws RuntimeException for unformattable handler result', function () { + $resource = RegisteredResource::make($this->resourceSchema, [ResourceHandlerFixture::class, 'returnUnformattableType']); + $resource->read($this->container, $this->testUri); +})->throws(\RuntimeException::class, "Cannot format resource read result for URI"); + + +it('can be serialized to array and deserialized', function () { + $original = RegisteredResource::make( + ResourceSchema::make( + 'uri://test', + 'my-resource', + 'desc', + 'app/foo', + ), + [ResourceHandlerFixture::class, 'getStaticText'], + true + ); + + $array = $original->toArray(); + + expect($array['schema']['uri'])->toBe('uri://test'); + expect($array['schema']['name'])->toBe('my-resource'); + expect($array['schema']['description'])->toBe('desc'); + expect($array['schema']['mimeType'])->toBe('app/foo'); + expect($array['handler'])->toBe([ResourceHandlerFixture::class, 'getStaticText']); + expect($array['isManual'])->toBeTrue(); + + $rehydrated = RegisteredResource::fromArray($array); + expect($rehydrated)->toBeInstanceOf(RegisteredResource::class); + expect($rehydrated->schema->uri)->toEqual($original->schema->uri); + expect($rehydrated->schema->name)->toEqual($original->schema->name); + expect($rehydrated->isManual)->toBeTrue(); +}); + +it('fromArray returns false on failure', function () { + $badData = ['schema' => ['uri' => 'fail']]; + expect(RegisteredResource::fromArray($badData))->toBeFalse(); +}); diff --git a/vendor/php-mcp/server/tests/Unit/Elements/RegisteredToolTest.php b/vendor/php-mcp/server/tests/Unit/Elements/RegisteredToolTest.php new file mode 100644 index 000000000..f132c3e92 --- /dev/null +++ b/vendor/php-mcp/server/tests/Unit/Elements/RegisteredToolTest.php @@ -0,0 +1,169 @@ +container = Mockery::mock(ContainerInterface::class); + $this->handlerInstance = new ToolHandlerFixture(); + $this->container->shouldReceive('get')->with(ToolHandlerFixture::class) + ->andReturn($this->handlerInstance)->byDefault(); + + $this->toolSchema = Tool::make( + name: 'test-tool', + inputSchema: ['type' => 'object', 'properties' => ['name' => ['type' => 'string']]] + ); + + $this->registeredTool = RegisteredTool::make( + $this->toolSchema, + [ToolHandlerFixture::class, 'greet'] + ); +}); + +it('constructs correctly and exposes schema', function () { + expect($this->registeredTool->schema)->toBe($this->toolSchema); + expect($this->registeredTool->handler)->toBe([ToolHandlerFixture::class, 'greet']); + expect($this->registeredTool->isManual)->toBeFalse(); +}); + +it('can be made as a manual registration', function () { + $manualTool = RegisteredTool::make($this->toolSchema, [ToolHandlerFixture::class, 'greet'], true); + expect($manualTool->isManual)->toBeTrue(); +}); + +it('calls the handler with prepared arguments', function () { + $tool = RegisteredTool::make( + Tool::make('sum-tool', ['type' => 'object', 'properties' => ['a' => ['type' => 'integer'], 'b' => ['type' => 'integer']]]), + [ToolHandlerFixture::class, 'sum'] + ); + $mockHandler = Mockery::mock(ToolHandlerFixture::class); + $mockHandler->shouldReceive('sum')->with(5, 10)->once()->andReturn(15); + $this->container->shouldReceive('get')->with(ToolHandlerFixture::class)->andReturn($mockHandler); + + $resultContents = $tool->call($this->container, ['a' => 5, 'b' => '10']); // '10' will be cast to int by prepareArguments + + expect($resultContents)->toBeArray()->toHaveCount(1); + expect($resultContents[0])->toBeInstanceOf(TextContent::class)->text->toBe('15'); +}); + +it('calls handler with no arguments if tool takes none and none provided', function () { + $tool = RegisteredTool::make( + Tool::make('no-args-tool', ['type' => 'object', 'properties' => []]), + [ToolHandlerFixture::class, 'noParamsTool'] + ); + $mockHandler = Mockery::mock(ToolHandlerFixture::class); + $mockHandler->shouldReceive('noParamsTool')->withNoArgs()->once()->andReturn(['status' => 'done']); + $this->container->shouldReceive('get')->with(ToolHandlerFixture::class)->andReturn($mockHandler); + + $resultContents = $tool->call($this->container, []); + expect($resultContents[0])->toBeInstanceOf(TextContent::class)->text->toBe(json_encode(['status' => 'done'], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); +}); + + +dataset('tool_handler_return_values', [ + 'string' => ['returnString', "This is a string result."], + 'integer' => ['returnInteger', "12345"], + 'float' => ['returnFloat', "67.89"], + 'boolean_true' => ['returnBooleanTrue', "true"], + 'boolean_false' => ['returnBooleanFalse', "false"], + 'null' => ['returnNull', "(null)"], + 'array_to_json' => ['returnArray', json_encode(['message' => 'Array result', 'data' => [1, 2, 3]], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)], + 'object_to_json' => ['returnStdClass', json_encode((object)['property' => "value"], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)], +]); + +it('formats various scalar and simple object/array handler results into TextContent', function (string $handlerMethod, string $expectedText) { + $tool = RegisteredTool::make( + Tool::make('format-test-tool', ['type' => 'object', 'properties' => []]), + [ToolHandlerFixture::class, $handlerMethod] + ); + + $resultContents = $tool->call($this->container, []); + + expect($resultContents)->toBeArray()->toHaveCount(1); + expect($resultContents[0])->toBeInstanceOf(TextContent::class)->text->toBe($expectedText); +})->with('tool_handler_return_values'); + +it('returns single Content object from handler as array with one Content object', function () { + $tool = RegisteredTool::make( + Tool::make('content-test-tool', ['type' => 'object', 'properties' => []]), + [ToolHandlerFixture::class, 'returnTextContent'] + ); + $resultContents = $tool->call($this->container, []); + + expect($resultContents)->toBeArray()->toHaveCount(1); + expect($resultContents[0])->toBeInstanceOf(TextContent::class)->text->toBe("Pre-formatted TextContent."); +}); + +it('returns array of Content objects from handler as is', function () { + $tool = RegisteredTool::make( + Tool::make('content-array-tool', ['type' => 'object', 'properties' => []]), + [ToolHandlerFixture::class, 'returnArrayOfContent'] + ); + $resultContents = $tool->call($this->container, []); + + expect($resultContents)->toBeArray()->toHaveCount(2); + expect($resultContents[0])->toBeInstanceOf(TextContent::class)->text->toBe("Part 1"); + expect($resultContents[1])->toBeInstanceOf(ImageContent::class)->data->toBe("imgdata"); +}); + +it('formats mixed array from handler into array of Content objects', function () { + $tool = RegisteredTool::make( + Tool::make('mixed-array-tool', ['type' => 'object', 'properties' => []]), + [ToolHandlerFixture::class, 'returnMixedArray'] + ); + $resultContents = $tool->call($this->container, []); + + expect($resultContents)->toBeArray()->toHaveCount(8); + + expect($resultContents[0])->toBeInstanceOf(TextContent::class)->text->toBe("A raw string"); + expect($resultContents[1])->toBeInstanceOf(TextContent::class)->text->toBe("A TextContent object"); // Original TextContent is preserved + expect($resultContents[2])->toBeInstanceOf(TextContent::class)->text->toBe("123"); + expect($resultContents[3])->toBeInstanceOf(TextContent::class)->text->toBe("true"); + expect($resultContents[4])->toBeInstanceOf(TextContent::class)->text->toBe("(null)"); + expect($resultContents[5])->toBeInstanceOf(TextContent::class)->text->toBe(json_encode(['nested_key' => 'nested_value', 'sub_array' => [4, 5]], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); + expect($resultContents[6])->toBeInstanceOf(ImageContent::class)->data->toBe("img_data_mixed"); // Original ImageContent is preserved + expect($resultContents[7])->toBeInstanceOf(TextContent::class)->text->toBe(json_encode((object)['obj_prop' => 'obj_val'], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); +}); + +it('formats empty array from handler into TextContent with "[]"', function () { + $tool = RegisteredTool::make( + Tool::make('empty-array-tool', ['type' => 'object', 'properties' => []]), + [ToolHandlerFixture::class, 'returnEmptyArray'] + ); + $resultContents = $tool->call($this->container, []); + + expect($resultContents)->toBeArray()->toHaveCount(1); + expect($resultContents[0])->toBeInstanceOf(TextContent::class)->text->toBe('[]'); +}); + +it('throws JsonException during formatResult if handler returns unencodable value', function () { + $tool = RegisteredTool::make( + Tool::make('unencodable-tool', ['type' => 'object', 'properties' => []]), + [ToolHandlerFixture::class, 'toolUnencodableResult'] + ); + $tool->call($this->container, []); +})->throws(JsonException::class); + +it('re-throws exceptions from handler execution wrapped in McpServerException from handle()', function () { + $tool = RegisteredTool::make( + Tool::make('exception-tool', ['type' => 'object', 'properties' => []]), + [ToolHandlerFixture::class, 'toolThatThrows'] + ); + + $this->container->shouldReceive('get')->with(ToolHandlerFixture::class)->once()->andReturn(new ToolHandlerFixture()); + + $tool->call($this->container, []); +})->throws(InvalidArgumentException::class, "Something went wrong in the tool."); diff --git a/vendor/php-mcp/server/tests/Unit/ProtocolTest.php b/vendor/php-mcp/server/tests/Unit/ProtocolTest.php new file mode 100644 index 000000000..138b37cf6 --- /dev/null +++ b/vendor/php-mcp/server/tests/Unit/ProtocolTest.php @@ -0,0 +1,542 @@ +expect($response)->toBeInstanceOf(Error::class); + test()->expect($response->id)->toBe($expectedId); + test()->expect($response->code)->toBe($expectedCode); + test()->expect($response->jsonrpc)->toBe('2.0'); +} + +function expectSuccessResponse(mixed $response, mixed $expectedResult, string|int|null $expectedId = 'req-proto-1'): void +{ + test()->expect($response)->toBeInstanceOf(Response::class); + test()->expect($response->id)->toBe($expectedId); + test()->expect($response->jsonrpc)->toBe('2.0'); + test()->expect($response->result)->toBe($expectedResult); +} + + +beforeEach(function () { + /** @var MockInterface&Registry $registry */ + $this->registry = Mockery::mock(Registry::class); + /** @var MockInterface&SessionManager $sessionManager */ + $this->sessionManager = Mockery::mock(SessionManager::class); + /** @var MockInterface&Dispatcher $dispatcher */ + $this->dispatcher = Mockery::mock(Dispatcher::class); + /** @var MockInterface&SubscriptionManager $subscriptionManager */ + $this->subscriptionManager = Mockery::mock(SubscriptionManager::class); + /** @var MockInterface&LoggerInterface $logger */ + $this->logger = Mockery::mock(LoggerInterface::class)->shouldIgnoreMissing(); + /** @var MockInterface&ServerTransportInterface $transport */ + $this->transport = Mockery::mock(ServerTransportInterface::class); + /** @var MockInterface&SessionInterface $session */ + $this->session = Mockery::mock(SessionInterface::class); + + /** @var MockInterface&LoopInterface $loop */ + $loop = Mockery::mock(LoopInterface::class); + /** @var MockInterface&CacheInterface $cache */ + $cache = Mockery::mock(CacheInterface::class); + /** @var MockInterface&ContainerInterface $container */ + $container = Mockery::mock(ContainerInterface::class); + + $this->configuration = new Configuration( + serverInfo: Implementation::make(SERVER_NAME_PROTO, SERVER_VERSION_PROTO), + capabilities: ServerCapabilities::make(), + logger: $this->logger, + loop: $loop, + cache: $cache, + container: $container + ); + + $this->sessionManager->shouldReceive('getSession')->with(SESSION_ID)->andReturn($this->session)->byDefault(); + $this->sessionManager->shouldReceive('on')->withAnyArgs()->byDefault(); + + $this->registry->shouldReceive('on')->withAnyArgs()->byDefault(); + + $this->session->shouldReceive('get')->with('initialized', false)->andReturn(true)->byDefault(); + $this->session->shouldReceive('save')->byDefault(); + + $this->transport->shouldReceive('on')->withAnyArgs()->byDefault(); + $this->transport->shouldReceive('removeListener')->withAnyArgs()->byDefault(); + $this->transport->shouldReceive('sendMessage') + ->withAnyArgs() + ->andReturn(resolve(null)) + ->byDefault(); + + $this->protocol = new Protocol( + $this->configuration, + $this->registry, + $this->sessionManager, + $this->dispatcher, + $this->subscriptionManager + ); + + $this->protocol->bindTransport($this->transport); +}); + +it('listens to SessionManager events on construction', function () { + $this->sessionManager->shouldHaveReceived('on')->with('session_deleted', Mockery::type('callable')); +}); + +it('listens to Registry events on construction', function () { + $this->registry->shouldHaveReceived('on')->with('list_changed', Mockery::type('callable')); +}); + +it('binds to a transport and attaches listeners', function () { + $newTransport = Mockery::mock(ServerTransportInterface::class); + $newTransport->shouldReceive('on')->with('message', Mockery::type('callable'))->once(); + $newTransport->shouldReceive('on')->with('client_connected', Mockery::type('callable'))->once(); + $newTransport->shouldReceive('on')->with('client_disconnected', Mockery::type('callable'))->once(); + $newTransport->shouldReceive('on')->with('error', Mockery::type('callable'))->once(); + + $this->protocol->bindTransport($newTransport); +}); + +it('unbinds from a previous transport when binding a new one', function () { + $this->transport->shouldReceive('removeListener')->times(4); + + $newTransport = Mockery::mock(ServerTransportInterface::class); + $newTransport->shouldReceive('on')->times(4); + + $this->protocol->bindTransport($newTransport); +}); + +it('unbinds transport and removes listeners', function () { + $this->transport->shouldReceive('removeListener')->with('message', Mockery::type('callable'))->once(); + $this->transport->shouldReceive('removeListener')->with('client_connected', Mockery::type('callable'))->once(); + $this->transport->shouldReceive('removeListener')->with('client_disconnected', Mockery::type('callable'))->once(); + $this->transport->shouldReceive('removeListener')->with('error', Mockery::type('callable'))->once(); + + $this->protocol->unbindTransport(); + + $reflection = new \ReflectionClass($this->protocol); + $transportProp = $reflection->getProperty('transport'); + $transportProp->setAccessible(true); + expect($transportProp->getValue($this->protocol))->toBeNull(); +}); + +it('processes a valid Request message', function () { + $request = createRequest('test/method', ['param' => 1]); + $result = new EmptyResult(); + $expectedResponse = Response::make($request->id, $result); + + $this->dispatcher->shouldReceive('handleRequest')->once() + ->with(Mockery::on(fn ($arg) => $arg instanceof Request && $arg->method === 'test/method'), $this->session) + ->andReturn($result); + + $this->transport->shouldReceive('sendMessage')->once() + ->with(Mockery::on(fn ($arg) => $arg instanceof Response && $arg->id === $request->id && $arg->result === $result), SESSION_ID, Mockery::any()) + ->andReturn(resolve(null)); + + $this->protocol->processMessage($request, SESSION_ID); + $this->session->shouldHaveReceived('save'); +}); + +it('processes a valid Notification message', function () { + $notification = createNotification('test/notify', ['data' => 'info']); + + $this->dispatcher->shouldReceive('handleNotification')->once() + ->with(Mockery::on(fn ($arg) => $arg instanceof Notification && $arg->method === 'test/notify'), $this->session) + ->andReturnNull(); + + $this->transport->shouldNotReceive('sendMessage'); + + $this->protocol->processMessage($notification, SESSION_ID); + $this->session->shouldHaveReceived('save'); +}); + +it('processes a BatchRequest with mixed requests and notifications', function () { + $req1 = createRequest('req/1', [], 'batch-id-1'); + $notif1 = createNotification('notif/1'); + $req2 = createRequest('req/2', [], 'batch-id-2'); + $batchRequest = new BatchRequest([$req1, $notif1, $req2]); + + $result1 = new EmptyResult(); + $result2 = new EmptyResult(); + + $this->dispatcher->shouldReceive('handleRequest')->once()->with(Mockery::on(fn (Request $r) => $r->id === 'batch-id-1'), $this->session)->andReturn($result1); + $this->dispatcher->shouldReceive('handleNotification')->once()->with(Mockery::on(fn (Notification $n) => $n->method === 'notif/1'), $this->session); + $this->dispatcher->shouldReceive('handleRequest')->once()->with(Mockery::on(fn (Request $r) => $r->id === 'batch-id-2'), $this->session)->andReturn($result2); + + + $this->transport->shouldReceive('sendMessage')->once() + ->with(Mockery::on(function (BatchResponse $response) use ($req1, $req2, $result1, $result2) { + expect(count($response->items))->toBe(2); + expect($response->items[0]->id)->toBe($req1->id); + expect($response->items[0]->result)->toBe($result1); + expect($response->items[1]->id)->toBe($req2->id); + expect($response->items[1]->result)->toBe($result2); + return true; + }), SESSION_ID, Mockery::any()) + ->andReturn(resolve(null)); + + $this->protocol->processMessage($batchRequest, SESSION_ID); + $this->session->shouldHaveReceived('save'); +}); + +it('processes a BatchRequest with only notifications and sends no response', function () { + $notif1 = createNotification('notif/only1'); + $notif2 = createNotification('notif/only2'); + $batchRequest = new BatchRequest([$notif1, $notif2]); + + $this->dispatcher->shouldReceive('handleNotification')->twice(); + $this->transport->shouldNotReceive('sendMessage'); + + $this->protocol->processMessage($batchRequest, SESSION_ID); + $this->session->shouldHaveReceived('save'); +}); + + +it('sends error response if session is not found', function () { + $request = createRequest('test/method'); + $this->sessionManager->shouldReceive('getSession')->with('unknown-client')->andReturn(null); + + $this->transport->shouldReceive('sendMessage')->once() + ->with(Mockery::on(function (Error $error) use ($request) { + expectErrorResponse($error, \PhpMcp\Schema\Constants::INVALID_REQUEST, $request->id); + expect($error->message)->toContain('Invalid or expired session'); + return true; + }), 'unknown-client', ['status_code' => 404, 'is_initialize_request' => false]) + ->andReturn(resolve(null)); + + $this->protocol->processMessage($request, 'unknown-client', ['is_initialize_request' => false]); + $this->session->shouldNotHaveReceived('save'); +}); + +it('sends error response if session is not initialized for non-initialize request', function () { + $request = createRequest('tools/list'); + $this->session->shouldReceive('get')->with('initialized', false)->andReturn(false); + + $this->transport->shouldReceive('sendMessage')->once() + ->with(Mockery::on(function (Error $error) use ($request) { + expectErrorResponse($error, \PhpMcp\Schema\Constants::INVALID_REQUEST, $request->id); + expect($error->message)->toContain('Client session not initialized'); + return true; + }), SESSION_ID, Mockery::any()) + ->andReturn(resolve(null)); + + $this->protocol->processMessage($request, SESSION_ID); +}); + +it('sends error response if capability for request method is disabled', function () { + $request = createRequest('tools/list'); + $configuration = new Configuration( + serverInfo: $this->configuration->serverInfo, + capabilities: ServerCapabilities::make(tools: false), + logger: $this->logger, + loop: $this->configuration->loop, + cache: $this->configuration->cache, + container: $this->configuration->container, + ); + + $protocol = new Protocol( + $configuration, + $this->registry, + $this->sessionManager, + $this->dispatcher, + $this->subscriptionManager + ); + + $protocol->bindTransport($this->transport); + + $this->transport->shouldReceive('sendMessage')->once() + ->with(Mockery::on(function (Error $error) use ($request) { + expectErrorResponse($error, \PhpMcp\Schema\Constants::METHOD_NOT_FOUND, $request->id); + expect($error->message)->toContain('Tools are not enabled'); + return true; + }), SESSION_ID, Mockery::any()) + ->andReturn(resolve(null)); + + $protocol->processMessage($request, SESSION_ID); +}); + +it('sends exceptions thrown while handling request as JSON-RPC error', function () { + $request = createRequest('fail/method'); + $exception = McpServerException::methodNotFound('fail/method'); + + $this->dispatcher->shouldReceive('handleRequest')->once()->andThrow($exception); + + $this->transport->shouldReceive('sendMessage')->once() + ->with(Mockery::on(function (Error $error) use ($request) { + expectErrorResponse($error, \PhpMcp\Schema\Constants::METHOD_NOT_FOUND, $request->id); + expect($error->message)->toContain('Method not found'); + return true; + }), SESSION_ID, Mockery::any()) + ->andReturn(resolve(null)); + + $this->protocol->processMessage($request, SESSION_ID); + + + $request = createRequest('explode/method'); + $exception = new \RuntimeException('Something bad happened'); + + $this->dispatcher->shouldReceive('handleRequest')->once()->andThrow($exception); + + $this->transport->shouldReceive('sendMessage')->once() + ->with(Mockery::on(function (Error $error) use ($request) { + expectErrorResponse($error, \PhpMcp\Schema\Constants::INTERNAL_ERROR, $request->id); + expect($error->message)->toContain('Internal error processing method explode/method'); + expect($error->data)->toBe('Something bad happened'); + return true; + }), SESSION_ID, Mockery::any()) + ->andReturn(resolve(null)); + + $this->protocol->processMessage($request, SESSION_ID); +}); + +it('sends a notification successfully', function () { + $notification = createNotification('event/occurred', ['value' => true]); + + $this->transport->shouldReceive('sendMessage')->once() + ->with($notification, SESSION_ID, []) + ->andReturn(resolve(null)); + + $promise = $this->protocol->sendNotification($notification, SESSION_ID); + await($promise); +}); + +it('rejects sending notification if transport not bound', function () { + $this->protocol->unbindTransport(); + $notification = createNotification('event/occurred'); + + $promise = $this->protocol->sendNotification($notification, SESSION_ID); + + await($promise->then(null, function (McpServerException $e) { + expect($e->getMessage())->toContain('Transport not bound'); + })); +}); + +it('rejects sending notification if transport send fails', function () { + $notification = createNotification('event/occurred'); + $transportException = new \PhpMcp\Server\Exception\TransportException('Send failed'); + $this->transport->shouldReceive('sendMessage')->once()->andReturn(reject($transportException)); + + $promise = $this->protocol->sendNotification($notification, SESSION_ID); + await($promise->then(null, function (McpServerException $e) use ($transportException) { + expect($e->getMessage())->toContain('Failed to send notification: Send failed'); + expect($e->getPrevious())->toBe($transportException); + })); +}); + +it('notifies resource updated to subscribers', function () { + $uri = 'test://resource/123'; + $subscribers = ['client-sub-1', 'client-sub-2']; + $this->subscriptionManager->shouldReceive('getSubscribers')->with($uri)->andReturn($subscribers); + + $expectedNotification = ResourceUpdatedNotification::make($uri); + + $this->transport->shouldReceive('sendMessage')->twice() + ->with(Mockery::on(function (Notification $notification) use ($expectedNotification) { + expect($notification->method)->toBe($expectedNotification->method); + expect($notification->params)->toBe($expectedNotification->params); + return true; + }), Mockery::anyOf(...$subscribers), []) + ->andReturn(resolve(null)); + + $this->protocol->notifyResourceUpdated($uri); +}); + +it('handles client connected event', function () { + $this->logger->shouldReceive('info')->with('Client connected', ['sessionId' => SESSION_ID])->once(); + $this->sessionManager->shouldReceive('createSession')->with(SESSION_ID)->once(); + + $this->protocol->handleClientConnected(SESSION_ID); +}); + +it('handles client disconnected event', function () { + $reason = 'Connection closed'; + $this->logger->shouldReceive('info')->with('Client disconnected', ['clientId' => SESSION_ID, 'reason' => $reason])->once(); + $this->sessionManager->shouldReceive('deleteSession')->with(SESSION_ID)->once(); + + $this->protocol->handleClientDisconnected(SESSION_ID, $reason); +}); + +it('handles transport error event with client ID', function () { + $error = new \RuntimeException('Socket error'); + $this->logger->shouldReceive('error') + ->with('Transport error for client', ['error' => 'Socket error', 'exception_class' => \RuntimeException::class, 'clientId' => SESSION_ID]) + ->once(); + + $this->protocol->handleTransportError($error, SESSION_ID); +}); + +it('handles transport error event without client ID', function () { + $error = new \RuntimeException('Listener setup failed'); + $this->logger->shouldReceive('error') + ->with('General transport error', ['error' => 'Listener setup failed', 'exception_class' => \RuntimeException::class]) + ->once(); + + $this->protocol->handleTransportError($error, null); +}); + +it('handles list changed event from registry and notifies subscribers', function (string $listType, string $expectedNotificationClass) { + $listChangeUri = "mcp://changes/{$listType}"; + $subscribers = ['client-sub-A', 'client-sub-B']; + + $this->subscriptionManager->shouldReceive('getSubscribers')->with($listChangeUri)->andReturn($subscribers); + $capabilities = ServerCapabilities::make( + toolsListChanged: true, + resourcesListChanged: true, + promptsListChanged: true, + ); + + $configuration = new Configuration( + serverInfo: $this->configuration->serverInfo, + capabilities: $capabilities, + logger: $this->logger, + loop: $this->configuration->loop, + cache: $this->configuration->cache, + container: $this->configuration->container, + ); + + $protocol = new Protocol( + $configuration, + $this->registry, + $this->sessionManager, + $this->dispatcher, + $this->subscriptionManager + ); + + $protocol->bindTransport($this->transport); + + $this->transport->shouldReceive('sendMessage') + ->with(Mockery::type($expectedNotificationClass), Mockery::anyOf(...$subscribers), []) + ->times(count($subscribers)) + ->andReturn(resolve(null)); + + $protocol->handleListChanged($listType); +})->with([ + 'tools' => ['tools', ToolListChangedNotification::class], + 'resources' => ['resources', ResourceListChangedNotification::class], +]); + +it('does not send list changed notification if capability is disabled', function (string $listType) { + $listChangeUri = "mcp://changes/{$listType}"; + $subscribers = ['client-sub-A']; + $this->subscriptionManager->shouldReceive('getSubscribers')->with($listChangeUri)->andReturn($subscribers); + + $caps = ServerCapabilities::make( + toolsListChanged: $listType !== 'tools', + resourcesListChanged: $listType !== 'resources', + promptsListChanged: $listType !== 'prompts', + ); + + $configuration = new Configuration( + serverInfo: $this->configuration->serverInfo, + capabilities: $caps, + logger: $this->logger, + loop: $this->configuration->loop, + cache: $this->configuration->cache, + container: $this->configuration->container, + ); + + $protocol = new Protocol( + $configuration, + $this->registry, + $this->sessionManager, + $this->dispatcher, + $this->subscriptionManager + ); + + $protocol->bindTransport($this->transport); + $this->transport->shouldNotReceive('sendMessage'); +})->with(['tools', 'resources', 'prompts',]); + + +it('allows initialize request when session not initialized', function () { + $request = createRequest('initialize', ['protocolVersion' => SUPPORTED_VERSION_PROTO]); + $this->session->shouldReceive('get')->with('initialized', false)->andReturn(false); + + $this->dispatcher->shouldReceive('handleRequest')->once() + ->with(Mockery::type(Request::class), $this->session) + ->andReturn(new EmptyResult()); + + $this->transport->shouldReceive('sendMessage')->once() + ->andReturn(resolve(null)); + + $this->protocol->processMessage($request, SESSION_ID); +}); + +it('allows initialize and ping regardless of capabilities', function (string $method) { + $request = createRequest($method); + $capabilities = ServerCapabilities::make( + tools: false, + resources: false, + prompts: false, + logging: false, + ); + $configuration = new Configuration( + serverInfo: $this->configuration->serverInfo, + capabilities: $capabilities, + logger: $this->logger, + loop: $this->configuration->loop, + cache: $this->configuration->cache, + container: $this->configuration->container, + ); + + $protocol = new Protocol( + $configuration, + $this->registry, + $this->sessionManager, + $this->dispatcher, + $this->subscriptionManager + ); + + $protocol->bindTransport($this->transport); + + $this->dispatcher->shouldReceive('handleRequest')->once()->andReturn(new EmptyResult()); + $this->transport->shouldReceive('sendMessage')->once() + ->andReturn(resolve(null)); + + $protocol->processMessage($request, SESSION_ID); +})->with(['initialize', 'ping']); diff --git a/vendor/php-mcp/server/tests/Unit/RegistryTest.php b/vendor/php-mcp/server/tests/Unit/RegistryTest.php new file mode 100644 index 000000000..1bab5fda6 --- /dev/null +++ b/vendor/php-mcp/server/tests/Unit/RegistryTest.php @@ -0,0 +1,592 @@ + 'object'], description: 'Desc ' . $name); +} + +function createTestResourceSchema(string $uri = 'test://res', string $name = 'test-res'): Resource +{ + return Resource::make(uri: $uri, name: $name, description: 'Desc ' . $name, mimeType: 'text/plain'); +} + +function createTestPromptSchema(string $name = 'test-prompt'): Prompt +{ + return Prompt::make(name: $name, description: 'Desc ' . $name, arguments: []); +} + +function createTestTemplateSchema(string $uriTemplate = 'tmpl://{id}', string $name = 'test-tmpl'): ResourceTemplate +{ + return ResourceTemplate::make(uriTemplate: $uriTemplate, name: $name, description: 'Desc ' . $name, mimeType: 'application/json'); +} + +beforeEach(function () { + /** @var MockInterface&LoggerInterface $logger */ + $this->logger = Mockery::mock(LoggerInterface::class)->shouldIgnoreMissing(); + /** @var MockInterface&CacheInterface $cache */ + $this->cache = Mockery::mock(CacheInterface::class); + + // Default cache behavior: miss on get, success on set/delete + $this->cache->allows('get')->with(DISCOVERED_CACHE_KEY_REG)->andReturn(null)->byDefault(); + $this->cache->allows('set')->with(DISCOVERED_CACHE_KEY_REG, Mockery::any())->andReturn(true)->byDefault(); + $this->cache->allows('delete')->with(DISCOVERED_CACHE_KEY_REG)->andReturn(true)->byDefault(); + + $this->registry = new Registry($this->logger, $this->cache); + $this->registryNoCache = new Registry($this->logger, null); +}); + +function getRegistryProperty(Registry $reg, string $propName) +{ + $reflector = new \ReflectionClass($reg); + $prop = $reflector->getProperty($propName); + $prop->setAccessible(true); + return $prop->getValue($reg); +} + +it('registers manual tool correctly', function () { + $toolSchema = createTestToolSchema('manual-tool-1'); + $this->registry->registerTool($toolSchema, ['HandlerClass', 'method'], true); + + $registeredTool = $this->registry->getTool('manual-tool-1'); + expect($registeredTool)->toBeInstanceOf(RegisteredTool::class) + ->and($registeredTool->schema)->toBe($toolSchema) + ->and($registeredTool->isManual)->toBeTrue(); + expect($this->registry->getTools())->toHaveKey('manual-tool-1'); +}); + +it('registers discovered tool correctly', function () { + $toolSchema = createTestToolSchema('discovered-tool-1'); + $this->registry->registerTool($toolSchema, ['HandlerClass', 'method'], false); + + $registeredTool = $this->registry->getTool('discovered-tool-1'); + expect($registeredTool)->toBeInstanceOf(RegisteredTool::class) + ->and($registeredTool->schema)->toBe($toolSchema) + ->and($registeredTool->isManual)->toBeFalse(); +}); + +it('registers manual resource correctly', function () { + $resourceSchema = createTestResourceSchema('manual://res/1'); + $this->registry->registerResource($resourceSchema, ['HandlerClass', 'method'], true); + + $registeredResource = $this->registry->getResource('manual://res/1'); + expect($registeredResource)->toBeInstanceOf(RegisteredResource::class) + ->and($registeredResource->schema)->toBe($resourceSchema) + ->and($registeredResource->isManual)->toBeTrue(); + expect($this->registry->getResources())->toHaveKey('manual://res/1'); +}); + +it('registers discovered resource correctly', function () { + $resourceSchema = createTestResourceSchema('discovered://res/1'); + $this->registry->registerResource($resourceSchema, ['HandlerClass', 'method'], false); + + $registeredResource = $this->registry->getResource('discovered://res/1'); + expect($registeredResource)->toBeInstanceOf(RegisteredResource::class) + ->and($registeredResource->schema)->toBe($resourceSchema) + ->and($registeredResource->isManual)->toBeFalse(); +}); + +it('registers manual prompt correctly', function () { + $promptSchema = createTestPromptSchema('manual-prompt-1'); + $this->registry->registerPrompt($promptSchema, ['HandlerClass', 'method'], [], true); + + $registeredPrompt = $this->registry->getPrompt('manual-prompt-1'); + expect($registeredPrompt)->toBeInstanceOf(RegisteredPrompt::class) + ->and($registeredPrompt->schema)->toBe($promptSchema) + ->and($registeredPrompt->isManual)->toBeTrue(); + expect($this->registry->getPrompts())->toHaveKey('manual-prompt-1'); +}); + +it('registers discovered prompt correctly', function () { + $promptSchema = createTestPromptSchema('discovered-prompt-1'); + $this->registry->registerPrompt($promptSchema, ['HandlerClass', 'method'], [], false); + + $registeredPrompt = $this->registry->getPrompt('discovered-prompt-1'); + expect($registeredPrompt)->toBeInstanceOf(RegisteredPrompt::class) + ->and($registeredPrompt->schema)->toBe($promptSchema) + ->and($registeredPrompt->isManual)->toBeFalse(); +}); + +it('registers manual resource template correctly', function () { + $templateSchema = createTestTemplateSchema('manual://tmpl/{id}'); + $this->registry->registerResourceTemplate($templateSchema, ['HandlerClass', 'method'], [], true); + + $registeredTemplate = $this->registry->getResourceTemplate('manual://tmpl/{id}'); + expect($registeredTemplate)->toBeInstanceOf(RegisteredResourceTemplate::class) + ->and($registeredTemplate->schema)->toBe($templateSchema) + ->and($registeredTemplate->isManual)->toBeTrue(); + expect($this->registry->getResourceTemplates())->toHaveKey('manual://tmpl/{id}'); +}); + +it('registers discovered resource template correctly', function () { + $templateSchema = createTestTemplateSchema('discovered://tmpl/{id}'); + $this->registry->registerResourceTemplate($templateSchema, ['HandlerClass', 'method'], [], false); + + $registeredTemplate = $this->registry->getResourceTemplate('discovered://tmpl/{id}'); + expect($registeredTemplate)->toBeInstanceOf(RegisteredResourceTemplate::class) + ->and($registeredTemplate->schema)->toBe($templateSchema) + ->and($registeredTemplate->isManual)->toBeFalse(); +}); + +test('getResource finds exact URI match before template match', function () { + $exactResourceSchema = createTestResourceSchema('test://item/exact'); + $templateSchema = createTestTemplateSchema('test://item/{itemId}'); + + $this->registry->registerResource($exactResourceSchema, ['H', 'm']); + $this->registry->registerResourceTemplate($templateSchema, ['H', 'm']); + + $found = $this->registry->getResource('test://item/exact'); + expect($found)->toBeInstanceOf(RegisteredResource::class) + ->and($found->schema->uri)->toBe('test://item/exact'); +}); + +test('getResource finds template match if no exact URI match', function () { + $templateSchema = createTestTemplateSchema('test://item/{itemId}'); + $this->registry->registerResourceTemplate($templateSchema, ['H', 'm']); + + $found = $this->registry->getResource('test://item/123'); + expect($found)->toBeInstanceOf(RegisteredResourceTemplate::class) + ->and($found->schema->uriTemplate)->toBe('test://item/{itemId}'); +}); + +test('getResource returns null if no match and templates excluded', function () { + $templateSchema = createTestTemplateSchema('test://item/{itemId}'); + $this->registry->registerResourceTemplate($templateSchema, ['H', 'm']); + + $found = $this->registry->getResource('test://item/123', false); + expect($found)->toBeNull(); +}); + +test('getResource returns null if no match at all', function () { + $found = $this->registry->getResource('nonexistent://uri'); + expect($found)->toBeNull(); +}); + +it('hasElements returns true if any manual elements exist', function () { + expect($this->registry->hasElements())->toBeFalse(); + $this->registry->registerTool(createTestToolSchema('manual-only'), ['H', 'm'], true); + expect($this->registry->hasElements())->toBeTrue(); +}); + +it('hasElements returns true if any discovered elements exist', function () { + expect($this->registry->hasElements())->toBeFalse(); + $this->registry->registerTool(createTestToolSchema('discovered-only'), ['H', 'm'], false); + expect($this->registry->hasElements())->toBeTrue(); +}); + +it('overrides existing discovered element with manual registration', function (string $type) { + $nameOrUri = $type === 'resource' ? 'conflict://res' : 'conflict-element'; + $templateUri = 'conflict://tmpl/{id}'; + + $discoveredSchema = match ($type) { + 'tool' => createTestToolSchema($nameOrUri), + 'resource' => createTestResourceSchema($nameOrUri), + 'prompt' => createTestPromptSchema($nameOrUri), + 'template' => createTestTemplateSchema($templateUri), + }; + $manualSchema = clone $discoveredSchema; + + match ($type) { + 'tool' => $this->registry->registerTool($discoveredSchema, ['H', 'm'], false), + 'resource' => $this->registry->registerResource($discoveredSchema, ['H', 'm'], false), + 'prompt' => $this->registry->registerPrompt($discoveredSchema, ['H', 'm'], [], false), + 'template' => $this->registry->registerResourceTemplate($discoveredSchema, ['H', 'm'], [], false), + }; + + match ($type) { + 'tool' => $this->registry->registerTool($manualSchema, ['H', 'm'], true), + 'resource' => $this->registry->registerResource($manualSchema, ['H', 'm'], true), + 'prompt' => $this->registry->registerPrompt($manualSchema, ['H', 'm'], [], true), + 'template' => $this->registry->registerResourceTemplate($manualSchema, ['H', 'm'], [], true), + }; + + $registeredElement = match ($type) { + 'tool' => $this->registry->getTool($nameOrUri), + 'resource' => $this->registry->getResource($nameOrUri), + 'prompt' => $this->registry->getPrompt($nameOrUri), + 'template' => $this->registry->getResourceTemplate($templateUri), + }; + + expect($registeredElement->schema)->toBe($manualSchema); + expect($registeredElement->isManual)->toBeTrue(); +})->with(['tool', 'resource', 'prompt', 'template']); + +it('does not override existing manual element with discovered registration', function (string $type) { + $nameOrUri = $type === 'resource' ? 'manual-priority://res' : 'manual-priority-element'; + $templateUri = 'manual-priority://tmpl/{id}'; + + $manualSchema = match ($type) { + 'tool' => createTestToolSchema($nameOrUri), + 'resource' => createTestResourceSchema($nameOrUri), + 'prompt' => createTestPromptSchema($nameOrUri), + 'template' => createTestTemplateSchema($templateUri), + }; + $discoveredSchema = clone $manualSchema; + + match ($type) { + 'tool' => $this->registry->registerTool($manualSchema, ['H', 'm'], true), + 'resource' => $this->registry->registerResource($manualSchema, ['H', 'm'], true), + 'prompt' => $this->registry->registerPrompt($manualSchema, ['H', 'm'], [], true), + 'template' => $this->registry->registerResourceTemplate($manualSchema, ['H', 'm'], [], true), + }; + + match ($type) { + 'tool' => $this->registry->registerTool($discoveredSchema, ['H', 'm'], false), + 'resource' => $this->registry->registerResource($discoveredSchema, ['H', 'm'], false), + 'prompt' => $this->registry->registerPrompt($discoveredSchema, ['H', 'm'], [], false), + 'template' => $this->registry->registerResourceTemplate($discoveredSchema, ['H', 'm'], [], false), + }; + + $registeredElement = match ($type) { + 'tool' => $this->registry->getTool($nameOrUri), + 'resource' => $this->registry->getResource($nameOrUri), + 'prompt' => $this->registry->getPrompt($nameOrUri), + 'template' => $this->registry->getResourceTemplate($templateUri), + }; + + expect($registeredElement->schema)->toBe($manualSchema); + expect($registeredElement->isManual)->toBeTrue(); +})->with(['tool', 'resource', 'prompt', 'template']); + + +it('loads discovered elements from cache correctly on construction', function () { + $toolSchema1 = createTestToolSchema('cached-tool-1'); + $resourceSchema1 = createTestResourceSchema('cached://res/1'); + $cachedData = [ + 'tools' => [$toolSchema1->name => json_encode(RegisteredTool::make($toolSchema1, ['H', 'm']))], + 'resources' => [$resourceSchema1->uri => json_encode(RegisteredResource::make($resourceSchema1, ['H', 'm']))], + 'prompts' => [], + 'resourceTemplates' => [], + ]; + $this->cache->shouldReceive('get')->with(DISCOVERED_CACHE_KEY_REG)->once()->andReturn($cachedData); + + $registry = new Registry($this->logger, $this->cache); + + expect($registry->getTool('cached-tool-1'))->toBeInstanceOf(RegisteredTool::class) + ->and($registry->getTool('cached-tool-1')->isManual)->toBeFalse(); + expect($registry->getResource('cached://res/1'))->toBeInstanceOf(RegisteredResource::class) + ->and($registry->getResource('cached://res/1')->isManual)->toBeFalse(); + expect($registry->hasElements())->toBeTrue(); +}); + +it('skips loading cached element if manual one with same key is registered later', function () { + $conflictName = 'conflict-tool'; + $cachedToolSchema = createTestToolSchema($conflictName); + $manualToolSchema = createTestToolSchema($conflictName); // Different instance + + $cachedData = ['tools' => [$conflictName => json_encode(RegisteredTool::make($cachedToolSchema, ['H', 'm']))]]; + $this->cache->shouldReceive('get')->with(DISCOVERED_CACHE_KEY_REG)->once()->andReturn($cachedData); + + $registry = new Registry($this->logger, $this->cache); + + expect($registry->getTool($conflictName)->schema->name)->toBe($cachedToolSchema->name); + expect($registry->getTool($conflictName)->isManual)->toBeFalse(); + + $registry->registerTool($manualToolSchema, ['H', 'm'], true); + + expect($registry->getTool($conflictName)->schema->name)->toBe($manualToolSchema->name); + expect($registry->getTool($conflictName)->isManual)->toBeTrue(); +}); + + +it('saves only non-manual elements to cache', function () { + $manualToolSchema = createTestToolSchema('manual-save'); + $discoveredToolSchema = createTestToolSchema('discovered-save'); + $expectedRegisteredDiscoveredTool = RegisteredTool::make($discoveredToolSchema, ['H', 'm'], false); + + $this->registry->registerTool($manualToolSchema, ['H', 'm'], true); + $this->registry->registerTool($discoveredToolSchema, ['H', 'm'], false); + + $expectedCachedData = [ + 'tools' => ['discovered-save' => json_encode($expectedRegisteredDiscoveredTool)], + 'resources' => [], + 'prompts' => [], + 'resourceTemplates' => [], + ]; + + $this->cache->shouldReceive('set')->once() + ->with(DISCOVERED_CACHE_KEY_REG, $expectedCachedData) + ->andReturn(true); + + $result = $this->registry->save(); + expect($result)->toBeTrue(); +}); + +it('does not attempt to save to cache if cache is null', function () { + $this->registryNoCache->registerTool(createTestToolSchema('discovered-no-cache'), ['H', 'm'], false); + $result = $this->registryNoCache->save(); + expect($result)->toBeFalse(); +}); + +it('handles invalid (non-array) data from cache gracefully during load', function () { + $this->cache->shouldReceive('get')->with(DISCOVERED_CACHE_KEY_REG)->once()->andReturn('this is not an array'); + $this->logger->shouldReceive('warning')->with(Mockery::pattern('/Invalid or missing data found in registry cache/'), Mockery::any())->once(); + + $registry = new Registry($this->logger, $this->cache); + + expect($registry->hasElements())->toBeFalse(); +}); + +it('handles cache unserialization errors gracefully during load', function () { + $badSerializedData = ['tools' => ['bad-tool' => 'not a serialized object']]; + $this->cache->shouldReceive('get')->with(DISCOVERED_CACHE_KEY_REG)->once()->andReturn($badSerializedData); + + $registry = new Registry($this->logger, $this->cache); + + expect($registry->hasElements())->toBeFalse(); +}); + +it('handles cache general exceptions during load gracefully', function () { + $this->cache->shouldReceive('get')->with(DISCOVERED_CACHE_KEY_REG)->once()->andThrow(new \RuntimeException('Cache unavailable')); + + $registry = new Registry($this->logger, $this->cache); + + expect($registry->hasElements())->toBeFalse(); +}); + +it('handles cache InvalidArgumentException during load gracefully', function () { + $this->cache->shouldReceive('get')->with(DISCOVERED_CACHE_KEY_REG)->once()->andThrow(new class() extends \Exception implements CacheInvalidArgumentException {}); + + $registry = new Registry($this->logger, $this->cache); + expect($registry->hasElements())->toBeFalse(); +}); + + +it('clears non-manual elements and deletes cache file', function () { + $this->registry->registerTool(createTestToolSchema('manual-clear'), ['H', 'm'], true); + $this->registry->registerTool(createTestToolSchema('discovered-clear'), ['H', 'm'], false); + + $this->cache->shouldReceive('delete')->with(DISCOVERED_CACHE_KEY_REG)->once()->andReturn(true); + + $this->registry->clear(); + + expect($this->registry->getTool('manual-clear'))->not->toBeNull(); + expect($this->registry->getTool('discovered-clear'))->toBeNull(); +}); + + +it('handles cache exceptions during clear gracefully', function () { + $this->registry->registerTool(createTestToolSchema('discovered-clear'), ['H', 'm'], false); + $this->cache->shouldReceive('delete')->with(DISCOVERED_CACHE_KEY_REG)->once()->andThrow(new \RuntimeException("Cache delete failed")); + + $this->registry->clear(); + + expect($this->registry->getTool('discovered-clear'))->toBeNull(); +}); + +it('emits list_changed event when a new tool is registered', function () { + $emitted = null; + $this->registry->on('list_changed', function ($listType) use (&$emitted) { + $emitted = $listType; + }); + + $this->registry->registerTool(createTestToolSchema('notifying-tool'), ['H', 'm']); + expect($emitted)->toBe('tools'); +}); + +it('emits list_changed event when a new resource is registered', function () { + $emitted = null; + $this->registry->on('list_changed', function ($listType) use (&$emitted) { + $emitted = $listType; + }); + + $this->registry->registerResource(createTestResourceSchema('notify://res'), ['H', 'm']); + expect($emitted)->toBe('resources'); +}); + +it('does not emit list_changed event if notifications are disabled', function () { + $this->registry->disableNotifications(); + $emitted = false; + $this->registry->on('list_changed', function () use (&$emitted) { + $emitted = true; + }); + + $this->registry->registerTool(createTestToolSchema('silent-tool'), ['H', 'm']); + expect($emitted)->toBeFalse(); + + $this->registry->enableNotifications(); +}); + +it('computes different hashes for different collections', function () { + $method = new \ReflectionMethod(Registry::class, 'computeHash'); + $method->setAccessible(true); + + $hash1 = $method->invoke($this->registry, ['a' => 1, 'b' => 2]); + $hash2 = $method->invoke($this->registry, ['b' => 2, 'a' => 1]); + $hash3 = $method->invoke($this->registry, ['a' => 1, 'c' => 3]); + + expect($hash1)->toBeString()->not->toBeEmpty(); + expect($hash2)->toBe($hash1); + expect($hash3)->not->toBe($hash1); + expect($method->invoke($this->registry, []))->toBe(''); +}); + +it('recomputes and emits list_changed only when content actually changes', function () { + $tool1 = createTestToolSchema('tool1'); + $tool2 = createTestToolSchema('tool2'); + $callCount = 0; + + $this->registry->on('list_changed', function ($listType) use (&$callCount) { + if ($listType === 'tools') { + $callCount++; + } + }); + + $this->registry->registerTool($tool1, ['H', 'm1']); + expect($callCount)->toBe(1); + + $this->registry->registerTool($tool1, ['H', 'm1']); + expect($callCount)->toBe(1); + + $this->registry->registerTool($tool2, ['H', 'm2']); + expect($callCount)->toBe(2); +}); + +it('registers tool with closure handler correctly', function () { + $toolSchema = createTestToolSchema('closure-tool'); + $closure = function (string $input): string { + return "processed: $input"; + }; + + $this->registry->registerTool($toolSchema, $closure, true); + + $registeredTool = $this->registry->getTool('closure-tool'); + expect($registeredTool)->toBeInstanceOf(RegisteredTool::class) + ->and($registeredTool->schema)->toBe($toolSchema) + ->and($registeredTool->isManual)->toBeTrue() + ->and($registeredTool->handler)->toBe($closure); +}); + +it('registers resource with closure handler correctly', function () { + $resourceSchema = createTestResourceSchema('closure://res'); + $closure = function (string $uri): array { + return [new \PhpMcp\Schema\Content\TextContent("Resource: $uri")]; + }; + + $this->registry->registerResource($resourceSchema, $closure, true); + + $registeredResource = $this->registry->getResource('closure://res'); + expect($registeredResource)->toBeInstanceOf(RegisteredResource::class) + ->and($registeredResource->schema)->toBe($resourceSchema) + ->and($registeredResource->isManual)->toBeTrue() + ->and($registeredResource->handler)->toBe($closure); +}); + +it('registers prompt with closure handler correctly', function () { + $promptSchema = createTestPromptSchema('closure-prompt'); + $closure = function (string $topic): array { + return [ + \PhpMcp\Schema\Content\PromptMessage::make( + \PhpMcp\Schema\Enum\Role::User, + new \PhpMcp\Schema\Content\TextContent("Tell me about $topic") + ) + ]; + }; + + $this->registry->registerPrompt($promptSchema, $closure, [], true); + + $registeredPrompt = $this->registry->getPrompt('closure-prompt'); + expect($registeredPrompt)->toBeInstanceOf(RegisteredPrompt::class) + ->and($registeredPrompt->schema)->toBe($promptSchema) + ->and($registeredPrompt->isManual)->toBeTrue() + ->and($registeredPrompt->handler)->toBe($closure); +}); + +it('registers resource template with closure handler correctly', function () { + $templateSchema = createTestTemplateSchema('closure://item/{id}'); + $closure = function (string $uri, string $id): array { + return [new \PhpMcp\Schema\Content\TextContent("Item $id from $uri")]; + }; + + $this->registry->registerResourceTemplate($templateSchema, $closure, [], true); + + $registeredTemplate = $this->registry->getResourceTemplate('closure://item/{id}'); + expect($registeredTemplate)->toBeInstanceOf(RegisteredResourceTemplate::class) + ->and($registeredTemplate->schema)->toBe($templateSchema) + ->and($registeredTemplate->isManual)->toBeTrue() + ->and($registeredTemplate->handler)->toBe($closure); +}); + +it('does not save closure handlers to cache', function () { + $closure = function (): string { + return 'test'; + }; + $arrayHandler = ['TestClass', 'testMethod']; + + $closureTool = createTestToolSchema('closure-tool'); + $arrayTool = createTestToolSchema('array-tool'); + + $this->registry->registerTool($closureTool, $closure, true); + $this->registry->registerTool($arrayTool, $arrayHandler, false); + + $expectedCachedData = [ + 'tools' => ['array-tool' => json_encode(RegisteredTool::make($arrayTool, $arrayHandler, false))], + 'resources' => [], + 'prompts' => [], + 'resourceTemplates' => [], + ]; + + $this->cache->shouldReceive('set')->once() + ->with(DISCOVERED_CACHE_KEY_REG, $expectedCachedData) + ->andReturn(true); + + $result = $this->registry->save(); + expect($result)->toBeTrue(); +}); + +it('handles static method handlers correctly', function () { + $toolSchema = createTestToolSchema('static-tool'); + $staticHandler = [TestStaticHandler::class, 'handle']; + + $this->registry->registerTool($toolSchema, $staticHandler, true); + + $registeredTool = $this->registry->getTool('static-tool'); + expect($registeredTool)->toBeInstanceOf(RegisteredTool::class) + ->and($registeredTool->handler)->toBe($staticHandler); +}); + +it('handles invokable class string handlers correctly', function () { + $toolSchema = createTestToolSchema('invokable-tool'); + $invokableHandler = TestInvokableHandler::class; + + $this->registry->registerTool($toolSchema, $invokableHandler, true); + + $registeredTool = $this->registry->getTool('invokable-tool'); + expect($registeredTool)->toBeInstanceOf(RegisteredTool::class) + ->and($registeredTool->handler)->toBe($invokableHandler); +}); + +// Test helper classes +class TestStaticHandler +{ + public static function handle(): string + { + return 'static result'; + } +} + +class TestInvokableHandler +{ + public function __invoke(): string + { + return 'invokable result'; + } +} diff --git a/vendor/php-mcp/server/tests/Unit/ServerBuilderTest.php b/vendor/php-mcp/server/tests/Unit/ServerBuilderTest.php new file mode 100644 index 000000000..ab46f621c --- /dev/null +++ b/vendor/php-mcp/server/tests/Unit/ServerBuilderTest.php @@ -0,0 +1,565 @@ + $id]; + } +} + +class SB_DummyCompletionProvider implements CompletionProviderInterface +{ + public function getCompletions(string $currentValue, SessionInterface $session): array + { + return []; + } +} + + +beforeEach(function () { + $this->builder = new ServerBuilder(); +}); + + +it('sets server info correctly', function () { + $this->builder->withServerInfo('MyServer', '1.2.3'); + $serverInfo = getPrivateProperty($this->builder, 'serverInfo'); + expect($serverInfo)->toBeInstanceOf(Implementation::class) + ->and($serverInfo->name)->toBe('MyServer') + ->and($serverInfo->version)->toBe('1.2.3'); +}); + +it('sets capabilities correctly', function () { + $capabilities = ServerCapabilities::make(toolsListChanged: true); + $this->builder->withCapabilities($capabilities); + expect(getPrivateProperty($this->builder, 'capabilities'))->toBe($capabilities); +}); + +it('sets pagination limit correctly', function () { + $this->builder->withPaginationLimit(100); + expect(getPrivateProperty($this->builder, 'paginationLimit'))->toBe(100); +}); + +it('sets logger correctly', function () { + $logger = Mockery::mock(LoggerInterface::class); + $this->builder->withLogger($logger); + expect(getPrivateProperty($this->builder, 'logger'))->toBe($logger); +}); + +it('sets cache correctly', function () { + $cache = Mockery::mock(CacheInterface::class); + $this->builder->withCache($cache); + expect(getPrivateProperty($this->builder, 'cache'))->toBe($cache); +}); + +it('sets session handler correctly', function () { + $handler = Mockery::mock(SessionHandlerInterface::class); + $this->builder->withSessionHandler($handler, 7200); + expect(getPrivateProperty($this->builder, 'sessionHandler'))->toBe($handler); + expect(getPrivateProperty($this->builder, 'sessionTtl'))->toBe(7200); +}); + +it('sets session driver to array correctly', function () { + $this->builder->withSession('array', 1800); + expect(getPrivateProperty($this->builder, 'sessionDriver'))->toBe('array'); + expect(getPrivateProperty($this->builder, 'sessionTtl'))->toBe(1800); +}); + +it('sets session driver to cache correctly', function () { + $this->builder->withSession('cache', 900); + expect(getPrivateProperty($this->builder, 'sessionDriver'))->toBe('cache'); + expect(getPrivateProperty($this->builder, 'sessionTtl'))->toBe(900); +}); + +it('uses default TTL when not specified for session', function () { + $this->builder->withSession('array'); + expect(getPrivateProperty($this->builder, 'sessionTtl'))->toBe(3600); +}); + +it('throws exception for invalid session driver', function () { + $this->builder->withSession('redis'); +})->throws(\InvalidArgumentException::class, "Unsupported session driver 'redis'. Only 'array' and 'cache' drivers are supported."); + +it('throws exception for cache session driver without cache during build', function () { + $this->builder + ->withServerInfo('Test', '1.0') + ->withSession('cache') + ->build(); +})->throws(ConfigurationException::class, 'Cache session driver requires a cache instance'); + +it('creates ArraySessionHandler when array driver is specified', function () { + $server = $this->builder + ->withServerInfo('Test', '1.0') + ->withSession('array', 1800) + ->build(); + + $sessionManager = $server->getSessionManager(); + $smReflection = new ReflectionClass(SessionManager::class); + $handlerProp = $smReflection->getProperty('handler'); + $handlerProp->setAccessible(true); + $handler = $handlerProp->getValue($sessionManager); + + expect($handler)->toBeInstanceOf(ArraySessionHandler::class); + expect($handler->ttl)->toBe(1800); +}); + +it('creates CacheSessionHandler when cache driver is specified', function () { + $cache = Mockery::mock(CacheInterface::class); + $cache->shouldReceive('get')->with('mcp_session_index', [])->andReturn([]); + + $server = $this->builder + ->withServerInfo('Test', '1.0') + ->withCache($cache) + ->withSession('cache', 900) + ->build(); + + $sessionManager = $server->getSessionManager(); + $smReflection = new ReflectionClass(SessionManager::class); + $handlerProp = $smReflection->getProperty('handler'); + $handlerProp->setAccessible(true); + $handler = $handlerProp->getValue($sessionManager); + + expect($handler)->toBeInstanceOf(CacheSessionHandler::class); + expect($handler->cache)->toBe($cache); + expect($handler->ttl)->toBe(900); +}); + +it('prefers custom session handler over session driver', function () { + $customHandler = Mockery::mock(SessionHandlerInterface::class); + + $server = $this->builder + ->withServerInfo('Test', '1.0') + ->withSession('array') + ->withSessionHandler($customHandler, 1200) + ->build(); + + $sessionManager = $server->getSessionManager(); + $smReflection = new ReflectionClass(SessionManager::class); + $handlerProp = $smReflection->getProperty('handler'); + $handlerProp->setAccessible(true); + + expect($handlerProp->getValue($sessionManager))->toBe($customHandler); +}); + + +it('sets container correctly', function () { + $container = Mockery::mock(ContainerInterface::class); + $this->builder->withContainer($container); + expect(getPrivateProperty($this->builder, 'container'))->toBe($container); +}); + +it('sets loop correctly', function () { + $loop = Mockery::mock(LoopInterface::class); + $this->builder->withLoop($loop); + expect(getPrivateProperty($this->builder, 'loop'))->toBe($loop); +}); + +it('stores manual tool registration data', function () { + $handler = [SB_DummyHandlerClass::class, 'handle']; + $this->builder->withTool($handler, 'my-tool', 'Tool desc'); + $manualTools = getPrivateProperty($this->builder, 'manualTools'); + expect($manualTools[0]['handler'])->toBe($handler) + ->and($manualTools[0]['name'])->toBe('my-tool') + ->and($manualTools[0]['description'])->toBe('Tool desc'); +}); + +it('stores manual resource registration data', function () { + $handler = [SB_DummyHandlerClass::class, 'handle']; + $this->builder->withResource($handler, 'res://resource', 'Resource name'); + $manualResources = getPrivateProperty($this->builder, 'manualResources'); + expect($manualResources[0]['handler'])->toBe($handler) + ->and($manualResources[0]['uri'])->toBe('res://resource') + ->and($manualResources[0]['name'])->toBe('Resource name'); +}); + +it('stores manual resource template registration data', function () { + $handler = [SB_DummyHandlerClass::class, 'handle']; + $this->builder->withResourceTemplate($handler, 'res://resource', 'Resource name'); + $manualResourceTemplates = getPrivateProperty($this->builder, 'manualResourceTemplates'); + expect($manualResourceTemplates[0]['handler'])->toBe($handler) + ->and($manualResourceTemplates[0]['uriTemplate'])->toBe('res://resource') + ->and($manualResourceTemplates[0]['name'])->toBe('Resource name'); +}); + +it('stores manual prompt registration data', function () { + $handler = [SB_DummyHandlerClass::class, 'handle']; + $this->builder->withPrompt($handler, 'my-prompt', 'Prompt desc'); + $manualPrompts = getPrivateProperty($this->builder, 'manualPrompts'); + expect($manualPrompts[0]['handler'])->toBe($handler) + ->and($manualPrompts[0]['name'])->toBe('my-prompt') + ->and($manualPrompts[0]['description'])->toBe('Prompt desc'); +}); + +it('throws ConfigurationException if server info not provided', function () { + $this->builder->build(); +})->throws(ConfigurationException::class, 'Server name and version must be provided'); + + +it('resolves default Logger, Loop, Container, SessionHandler if not provided', function () { + $server = $this->builder->withServerInfo('Test', '1.0')->build(); + $config = $server->getConfiguration(); + + expect($config->logger)->toBeInstanceOf(NullLogger::class); + expect($config->loop)->toBeInstanceOf(LoopInterface::class); + expect($config->container)->toBeInstanceOf(BasicContainer::class); + + $sessionManager = $server->getSessionManager(); + $smReflection = new ReflectionClass(SessionManager::class); + $handlerProp = $smReflection->getProperty('handler'); + $handlerProp->setAccessible(true); + expect($handlerProp->getValue($sessionManager))->toBeInstanceOf(ArraySessionHandler::class); +}); + +it('builds Server with correct Configuration, Registry, Protocol, SessionManager', function () { + $logger = new NullLogger(); + $loop = Mockery::mock(LoopInterface::class)->shouldIgnoreMissing(); + $cache = Mockery::mock(CacheInterface::class); + $container = Mockery::mock(ContainerInterface::class); + $sessionHandler = Mockery::mock(SessionHandlerInterface::class); + $capabilities = ServerCapabilities::make(promptsListChanged: true, resourcesListChanged: true); + + $loop->shouldReceive('addPeriodicTimer')->with(300, Mockery::type('callable'))->andReturn(Mockery::mock(TimerInterface::class)); + + $server = $this->builder + ->withServerInfo('FullBuild', '3.0') + ->withLogger($logger) + ->withLoop($loop) + ->withCache($cache) + ->withContainer($container) + ->withSessionHandler($sessionHandler) + ->withCapabilities($capabilities) + ->withPaginationLimit(75) + ->build(); + + expect($server)->toBeInstanceOf(Server::class); + + $config = $server->getConfiguration(); + expect($config->serverInfo->name)->toBe('FullBuild'); + expect($config->serverInfo->version)->toBe('3.0'); + expect($config->capabilities)->toBe($capabilities); + expect($config->logger)->toBe($logger); + expect($config->loop)->toBe($loop); + expect($config->cache)->toBe($cache); + expect($config->container)->toBe($container); + expect($config->paginationLimit)->toBe(75); + + expect($server->getRegistry())->toBeInstanceOf(Registry::class); + expect($server->getProtocol())->toBeInstanceOf(Protocol::class); + expect($server->getSessionManager())->toBeInstanceOf(SessionManager::class); + $smReflection = new ReflectionClass($server->getSessionManager()); + $handlerProp = $smReflection->getProperty('handler'); + $handlerProp->setAccessible(true); + expect($handlerProp->getValue($server->getSessionManager()))->toBe($sessionHandler); +}); + +it('registers manual tool successfully during build', function () { + $handler = [SB_DummyHandlerClass::class, 'handle']; + + $server = $this->builder + ->withServerInfo('ManualToolTest', '1.0') + ->withTool($handler, 'test-manual-tool', 'A test tool') + ->build(); + + $registry = $server->getRegistry(); + $tool = $registry->getTool('test-manual-tool'); + + expect($tool)->toBeInstanceOf(RegisteredTool::class); + expect($tool->isManual)->toBeTrue(); + expect($tool->schema->name)->toBe('test-manual-tool'); + expect($tool->schema->description)->toBe('A test tool'); + expect($tool->schema->inputSchema)->toEqual(['type' => 'object', 'properties' => ['arg' => ['type' => 'string']], 'required' => ['arg']]); + expect($tool->handler)->toBe($handler); +}); + +it('infers tool name from invokable class if not provided', function () { + $handler = SB_DummyInvokableClass::class; + + $server = $this->builder + ->withServerInfo('Test', '1.0') + ->withTool($handler) + ->build(); + + $tool = $server->getRegistry()->getTool('SB_DummyInvokableClass'); + expect($tool)->not->toBeNull(); + expect($tool->schema->name)->toBe('SB_DummyInvokableClass'); +}); + +it('registers tool with closure handler', function () { + $closure = function (string $message): string { + return "Hello, $message!"; + }; + + $server = $this->builder + ->withServerInfo('ClosureTest', '1.0') + ->withTool($closure, 'greet-tool', 'A greeting tool') + ->build(); + + $tool = $server->getRegistry()->getTool('greet-tool'); + expect($tool)->toBeInstanceOf(RegisteredTool::class); + expect($tool->isManual)->toBeTrue(); + expect($tool->schema->name)->toBe('greet-tool'); + expect($tool->schema->description)->toBe('A greeting tool'); + expect($tool->handler)->toBe($closure); + expect($tool->schema->inputSchema)->toEqual([ + 'type' => 'object', + 'properties' => ['message' => ['type' => 'string']], + 'required' => ['message'] + ]); +}); + +it('registers tool with static method handler', function () { + $handler = [SB_DummyHandlerClass::class, 'handle']; + + $server = $this->builder + ->withServerInfo('StaticTest', '1.0') + ->withTool($handler, 'static-tool', 'A static method tool') + ->build(); + + $tool = $server->getRegistry()->getTool('static-tool'); + expect($tool)->toBeInstanceOf(RegisteredTool::class); + expect($tool->isManual)->toBeTrue(); + expect($tool->schema->name)->toBe('static-tool'); + expect($tool->handler)->toBe($handler); +}); + +it('registers resource with closure handler', function () { + $closure = function (string $id): array { + return [ + 'uri' => "res://item/$id", + 'name' => "Item $id", + 'mimeType' => 'application/json' + ]; + }; + + $server = $this->builder + ->withServerInfo('ResourceTest', '1.0') + ->withResource($closure, 'res://items/{id}', 'dynamic_resource') + ->build(); + + $resource = $server->getRegistry()->getResource('res://items/{id}'); + expect($resource)->not->toBeNull(); + expect($resource->handler)->toBe($closure); + expect($resource->isManual)->toBeTrue(); +}); + +it('registers prompt with closure handler', function () { + $closure = function (string $topic): array { + return [ + 'role' => 'user', + 'content' => ['type' => 'text', 'text' => "Tell me about $topic"] + ]; + }; + + $server = $this->builder + ->withServerInfo('PromptTest', '1.0') + ->withPrompt($closure, 'topic-prompt', 'A topic-based prompt') + ->build(); + + $prompt = $server->getRegistry()->getPrompt('topic-prompt'); + expect($prompt)->not->toBeNull(); + expect($prompt->handler)->toBe($closure); + expect($prompt->isManual)->toBeTrue(); +}); + +it('infers closure tool name automatically', function () { + $closure = function (int $count): array { + return ['count' => $count]; + }; + + $server = $this->builder + ->withServerInfo('AutoNameTest', '1.0') + ->withTool($closure) + ->build(); + + $tools = $server->getRegistry()->getTools(); + expect($tools)->toHaveCount(1); + + $toolName = array_keys($tools)[0]; + expect($toolName)->toStartWith('closure_tool_'); + + $tool = $server->getRegistry()->getTool($toolName); + expect($tool->handler)->toBe($closure); +}); + +it('generates unique names for multiple closures', function () { + $closure1 = function (string $a): string { + return $a; + }; + $closure2 = function (int $b): int { + return $b; + }; + + $server = $this->builder + ->withServerInfo('MultiClosureTest', '1.0') + ->withTool($closure1) + ->withTool($closure2) + ->build(); + + $tools = $server->getRegistry()->getTools(); + expect($tools)->toHaveCount(2); + + $toolNames = array_keys($tools); + expect($toolNames[0])->toStartWith('closure_tool_'); + expect($toolNames[1])->toStartWith('closure_tool_'); + expect($toolNames[0])->not->toBe($toolNames[1]); +}); + +it('infers prompt arguments and completion providers for manual prompt', function () { + $handler = [SB_DummyHandlerClass::class, 'handlerWithCompletion']; + + $server = $this->builder + ->withServerInfo('Test', '1.0') + ->withPrompt($handler, 'myPrompt') + ->build(); + + $prompt = $server->getRegistry()->getPrompt('myPrompt'); + expect($prompt)->toBeInstanceOf(RegisteredPrompt::class); + expect($prompt->schema->arguments)->toHaveCount(2); + expect($prompt->schema->arguments[0]->name)->toBe('name'); + expect($prompt->schema->arguments[1]->name)->toBe('uriParam'); + expect($prompt->completionProviders['uriParam'])->toBe(SB_DummyCompletionProvider::class); +}); + +// Add test fixtures for enhanced completion providers +class SB_DummyHandlerWithEnhancedCompletion +{ + public function handleWithListCompletion( + #[CompletionProvider(values: ['option1', 'option2', 'option3'])] + string $choice + ): array { + return [['role' => 'user', 'content' => "Selected: {$choice}"]]; + } + + public function handleWithEnumCompletion( + #[CompletionProvider(enum: SB_TestEnum::class)] + string $status + ): array { + return [['role' => 'user', 'content' => "Status: {$status}"]]; + } +} + +enum SB_TestEnum: string +{ + case PENDING = 'pending'; + case ACTIVE = 'active'; + case INACTIVE = 'inactive'; +} + +it('creates ListCompletionProvider for values attribute in manual registration', function () { + $handler = [SB_DummyHandlerWithEnhancedCompletion::class, 'handleWithListCompletion']; + + $server = $this->builder + ->withServerInfo('Test', '1.0') + ->withPrompt($handler, 'listPrompt') + ->build(); + + $prompt = $server->getRegistry()->getPrompt('listPrompt'); + expect($prompt->completionProviders['choice'])->toBeInstanceOf(\PhpMcp\Server\Defaults\ListCompletionProvider::class); +}); + +it('creates EnumCompletionProvider for enum attribute in manual registration', function () { + $handler = [SB_DummyHandlerWithEnhancedCompletion::class, 'handleWithEnumCompletion']; + + $server = $this->builder + ->withServerInfo('Test', '1.0') + ->withPrompt($handler, 'enumPrompt') + ->build(); + + $prompt = $server->getRegistry()->getPrompt('enumPrompt'); + expect($prompt->completionProviders['status'])->toBeInstanceOf(\PhpMcp\Server\Defaults\EnumCompletionProvider::class); +}); + +// it('throws DefinitionException if HandlerResolver fails for a manual element', function () { +// $handler = ['NonExistentClass', 'method']; + +// $server = $this->builder +// ->withServerInfo('Test', '1.0') +// ->withTool($handler, 'badTool') +// ->build(); +// })->throws(DefinitionException::class, '1 error(s) occurred during manual element registration'); + + +it('builds successfully with minimal valid config', function () { + $server = $this->builder + ->withServerInfo('TS-Compatible', '0.1') + ->build(); + expect($server)->toBeInstanceOf(Server::class); +}); + +it('can be built multiple times with different configurations', function () { + $builder = new ServerBuilder(); + + $server1 = $builder + ->withServerInfo('ServerOne', '1.0') + ->withTool([SB_DummyHandlerClass::class, 'handle'], 'toolOne') + ->build(); + + $server2 = $builder + ->withServerInfo('ServerTwo', '2.0') + ->withTool([SB_DummyHandlerClass::class, 'noArgsHandler'], 'toolTwo') + ->build(); + + expect($server1->getConfiguration()->serverInfo->name)->toBe('ServerOne'); + $registry1 = $server1->getRegistry(); + expect($registry1->getTool('toolOne'))->not->toBeNull(); + expect($registry1->getTool('toolTwo'))->toBeNull(); + + expect($server2->getConfiguration()->serverInfo->name)->toBe('ServerTwo'); + $registry2 = $server2->getRegistry(); + expect($registry2->getTool('toolOne'))->not->toBeNull(); + expect($registry2->getTool('toolTwo'))->not->toBeNull(); + + $builder3 = new ServerBuilder(); + $server3 = $builder3->withServerInfo('ServerThree', '3.0')->build(); + expect($server3->getRegistry()->hasElements())->toBeFalse(); +}); diff --git a/vendor/php-mcp/server/tests/Unit/ServerTest.php b/vendor/php-mcp/server/tests/Unit/ServerTest.php new file mode 100644 index 000000000..7e4c4468a --- /dev/null +++ b/vendor/php-mcp/server/tests/Unit/ServerTest.php @@ -0,0 +1,275 @@ +logger = Mockery::mock(LoggerInterface::class)->shouldIgnoreMissing(); + /** @var MockInterface&LoopInterface $loop */ + $this->loop = Mockery::mock(LoopInterface::class)->shouldIgnoreMissing(); + /** @var MockInterface&CacheInterface $cache */ + $this->cache = Mockery::mock(CacheInterface::class); + /** @var MockInterface&ContainerInterface $container */ + $this->container = Mockery::mock(ContainerInterface::class); + + $this->configuration = new Configuration( + serverInfo: Implementation::make('TestServerInstance', '1.0'), + capabilities: ServerCapabilities::make(), + logger: $this->logger, + loop: $this->loop, + cache: $this->cache, + container: $this->container + ); + + /** @var MockInterface&Registry $registry */ + $this->registry = Mockery::mock(Registry::class); + /** @var MockInterface&Protocol $protocol */ + $this->protocol = Mockery::mock(Protocol::class); + /** @var MockInterface&Discoverer $discoverer */ + $this->discoverer = Mockery::mock(Discoverer::class); + + $this->sessionManager = new SessionManager(new ArraySessionHandler(), $this->logger, $this->loop); + + $this->server = new Server( + $this->configuration, + $this->registry, + $this->protocol, + $this->sessionManager + ); + + $this->registry->allows('hasElements')->withNoArgs()->andReturn(false)->byDefault(); + $this->registry->allows('clear')->withAnyArgs()->byDefault(); + $this->registry->allows('save')->withAnyArgs()->andReturn(true)->byDefault(); +}); + +afterEach(function () { + $this->sessionManager->stopGcTimer(); +}); + +it('provides getters for core components', function () { + expect($this->server->getConfiguration())->toBe($this->configuration); + expect($this->server->getRegistry())->toBe($this->registry); + expect($this->server->getProtocol())->toBe($this->protocol); + expect($this->server->getSessionManager())->toBe($this->sessionManager); +}); + +it('provides a static make method returning ServerBuilder', function () { + expect(Server::make())->toBeInstanceOf(\PhpMcp\Server\ServerBuilder::class); +}); + +it('skips discovery if already run and not forced', function () { + $reflector = new \ReflectionClass($this->server); + $prop = $reflector->getProperty('discoveryRan'); + $prop->setAccessible(true); + $prop->setValue($this->server, true); + + $this->registry->shouldNotReceive('clear'); + $this->discoverer->shouldNotReceive('discover'); + $this->registry->shouldNotReceive('save'); + + $this->server->discover(sys_get_temp_dir(), discoverer: $this->discoverer); + $this->logger->shouldHaveReceived('debug')->with('Discovery skipped: Already run or loaded from cache.'); +}); + +it('forces discovery even if already run, calling injected discoverer', function () { + $reflector = new \ReflectionClass($this->server); + $prop = $reflector->getProperty('discoveryRan'); + $prop->setAccessible(true); + $prop->setValue($this->server, true); + + $basePath = realpath(sys_get_temp_dir()); + $scanDirs = ['.', 'src']; + + + $this->registry->shouldReceive('clear')->once(); + $this->discoverer->shouldReceive('discover') + ->with($basePath, $scanDirs, Mockery::type('array')) + ->once(); + $this->registry->shouldReceive('save')->once()->andReturn(true); + + $this->server->discover($basePath, $scanDirs, [], force: true, discoverer: $this->discoverer); + + expect($prop->getValue($this->server))->toBeTrue(); +}); + +it('calls registry clear and discoverer, then saves to cache by default', function () { + $basePath = realpath(sys_get_temp_dir()); + $scanDirs = ['app', 'lib']; + $userExcludeDirs = ['specific_exclude']; + $finalExcludeDirs = array_unique(array_merge( + ['vendor', 'tests', 'test', 'storage', 'cache', 'samples', 'docs', 'node_modules', '.git', '.svn'], + $userExcludeDirs + )); + + + $this->registry->shouldReceive('clear')->once(); + $this->discoverer->shouldReceive('discover') + ->with($basePath, $scanDirs, Mockery::on(function ($arg) use ($finalExcludeDirs) { + expect($arg)->toBeArray(); + expect($arg)->toEqualCanonicalizing($finalExcludeDirs); + return true; + })) + ->once(); + $this->registry->shouldReceive('save')->once()->andReturn(true); + + $this->server->discover($basePath, $scanDirs, $userExcludeDirs, discoverer: $this->discoverer); + + $reflector = new \ReflectionClass($this->server); + $prop = $reflector->getProperty('discoveryRan'); + $prop->setAccessible(true); + expect($prop->getValue($this->server))->toBeTrue(); +}); + +it('does not save to cache if saveToCache is false', function () { + $basePath = realpath(sys_get_temp_dir()); + + $this->registry->shouldReceive('clear')->once(); + $this->discoverer->shouldReceive('discover')->once(); + $this->registry->shouldNotReceive('save'); + + $this->server->discover($basePath, saveToCache: false, discoverer: $this->discoverer); +}); + +it('throws InvalidArgumentException for bad base path in discover', function () { + $this->discoverer->shouldNotReceive('discover'); + $this->server->discover('/non/existent/path/for/sure/I/hope', discoverer: $this->discoverer); +})->throws(\InvalidArgumentException::class, 'Invalid discovery base path'); + +it('throws DiscoveryException if Discoverer fails during discovery', function () { + $basePath = realpath(sys_get_temp_dir()); + + $this->registry->shouldReceive('clear')->once(); + $this->discoverer->shouldReceive('discover')->once()->andThrow(new \RuntimeException('Filesystem error')); + $this->registry->shouldNotReceive('save'); + + $this->server->discover($basePath, discoverer: $this->discoverer); +})->throws(DiscoveryException::class, 'Element discovery failed: Filesystem error'); + +it('resets discoveryRan flag on Discoverer failure', function () { + $basePath = realpath(sys_get_temp_dir()); + $this->registry->shouldReceive('clear')->once(); + $this->discoverer->shouldReceive('discover')->once()->andThrow(new \RuntimeException('Failure')); + + try { + $this->server->discover($basePath, discoverer: $this->discoverer); + } catch (DiscoveryException $e) { + // Expected + } + + $reflector = new \ReflectionClass($this->server); + $prop = $reflector->getProperty('discoveryRan'); + $prop->setAccessible(true); + expect($prop->getValue($this->server))->toBeFalse(); +}); + + +// --- Listening Tests --- +it('throws LogicException if listen is called when already listening', function () { + $transport = Mockery::mock(ServerTransportInterface::class)->shouldIgnoreMissing(); + $this->protocol->shouldReceive('bindTransport')->with($transport)->once(); + + $this->server->listen($transport, false); + $this->server->listen($transport, false); +})->throws(LogicException::class, 'Server is already listening'); + +it('warns if no elements and discovery not run when listen is called', function () { + $transport = Mockery::mock(ServerTransportInterface::class)->shouldIgnoreMissing(); + $this->protocol->shouldReceive('bindTransport')->with($transport)->once(); + + $this->registry->shouldReceive('hasElements')->andReturn(false); + + $this->logger->shouldReceive('warning') + ->once() + ->with(Mockery::pattern('/Starting listener, but no MCP elements are registered and discovery has not been run/')); + + $this->server->listen($transport, false); +}); + +it('injects logger and loop into aware transports during listen', function () { + $transport = Mockery::mock(ServerTransportInterface::class, LoggerAwareInterface::class, LoopAwareInterface::class); + $transport->shouldReceive('setLogger')->with($this->logger)->once(); + $transport->shouldReceive('setLoop')->with($this->loop)->once(); + $transport->shouldReceive('on', 'once', 'listen', 'emit', 'close', 'removeAllListeners')->withAnyArgs(); + $this->protocol->shouldReceive('bindTransport', 'unbindTransport')->withAnyArgs(); + + $this->server->listen($transport); +}); + +it('binds protocol, starts transport listener, and runs loop by default', function () { + $transport = Mockery::mock(ServerTransportInterface::class)->shouldIgnoreMissing(); + $transport->shouldReceive('listen')->once(); + $this->protocol->shouldReceive('bindTransport')->with($transport)->once(); + $this->loop->shouldReceive('run')->once(); + $this->protocol->shouldReceive('unbindTransport')->once(); + + $this->server->listen($transport); + expect(getPrivateProperty($this->server, 'isListening'))->toBeFalse(); +}); + +it('does not run loop if runLoop is false in listen', function () { + $transport = Mockery::mock(ServerTransportInterface::class)->shouldIgnoreMissing(); + $this->protocol->shouldReceive('bindTransport')->with($transport)->once(); + + $this->loop->shouldNotReceive('run'); + + $this->server->listen($transport, runLoop: false); + expect(getPrivateProperty($this->server, 'isListening'))->toBeTrue(); + + $this->protocol->shouldReceive('unbindTransport'); + $transport->shouldReceive('removeAllListeners'); + $transport->shouldReceive('close'); + $this->server->endListen($transport); +}); + +it('calls endListen if transport listen throws immediately', function () { + $transport = Mockery::mock(ServerTransportInterface::class)->shouldIgnoreMissing(); + $transport->shouldReceive('listen')->once()->andThrow(new \RuntimeException("Port in use")); + $this->protocol->shouldReceive('bindTransport')->once(); + $this->protocol->shouldReceive('unbindTransport')->once(); + + $this->loop->shouldNotReceive('run'); + + try { + $this->server->listen($transport); + } catch (\RuntimeException $e) { + expect($e->getMessage())->toBe("Port in use"); + } + expect(getPrivateProperty($this->server, 'isListening'))->toBeFalse(); +}); + +it('endListen unbinds protocol and closes transport if listening', function () { + $transport = Mockery::mock(ServerTransportInterface::class); + $reflector = new \ReflectionClass($this->server); + $prop = $reflector->getProperty('isListening'); + $prop->setAccessible(true); + $prop->setValue($this->server, true); + + $this->protocol->shouldReceive('unbindTransport')->once(); + $transport->shouldReceive('removeAllListeners')->with('close')->once(); + $transport->shouldReceive('close')->once(); + + $this->server->endListen($transport); + expect($prop->getValue($this->server))->toBeFalse(); +}); diff --git a/vendor/php-mcp/server/tests/Unit/Session/ArraySessionHandlerTest.php b/vendor/php-mcp/server/tests/Unit/Session/ArraySessionHandlerTest.php new file mode 100644 index 000000000..85d0a5ec0 --- /dev/null +++ b/vendor/php-mcp/server/tests/Unit/Session/ArraySessionHandlerTest.php @@ -0,0 +1,216 @@ +fixedClock = new FixedClock(); + $this->handler = new ArraySessionHandler(DEFAULT_TTL_ARRAY, $this->fixedClock); +}); + +it('implements SessionHandlerInterface', function () { + expect($this->handler)->toBeInstanceOf(SessionHandlerInterface::class); +}); + +it('constructs with a default TTL and SystemClock if no clock provided', function () { + $handler = new ArraySessionHandler(); + expect($handler->ttl)->toBe(DEFAULT_TTL_ARRAY); + $reflection = new \ReflectionClass($handler); + $clockProp = $reflection->getProperty('clock'); + $clockProp->setAccessible(true); + expect($clockProp->getValue($handler))->toBeInstanceOf(SystemClock::class); +}); + +it('constructs with a custom TTL and injected clock', function () { + $customTtl = 1800; + $clock = new FixedClock(); + $handler = new ArraySessionHandler($customTtl, $clock); + expect($handler->ttl)->toBe($customTtl); + $reflection = new \ReflectionClass($handler); + $clockProp = $reflection->getProperty('clock'); + $clockProp->setAccessible(true); + expect($clockProp->getValue($handler))->toBe($clock); +}); + +it('writes session data and reads it back correctly', function () { + $writeResult = $this->handler->write(SESSION_ID_ARRAY_1, SESSION_DATA_1); + expect($writeResult)->toBeTrue(); + + $readData = $this->handler->read(SESSION_ID_ARRAY_1); + expect($readData)->toBe(SESSION_DATA_1); +}); + +it('returns false when reading a non-existent session', function () { + $readData = $this->handler->read('non-existent-session-id'); + expect($readData)->toBeFalse(); +}); + +it('overwrites existing session data on subsequent write', function () { + $this->handler->write(SESSION_ID_ARRAY_1, SESSION_DATA_1); + $updatedData = '{"user_id":101,"cart":{"items":[{"id":"prod_A","qty":3}],"total":175.25},"theme":"light"}'; + $this->handler->write(SESSION_ID_ARRAY_1, $updatedData); + + $readData = $this->handler->read(SESSION_ID_ARRAY_1); + expect($readData)->toBe($updatedData); +}); + +it('returns false and removes data when reading an expired session due to handler TTL', function () { + $ttl = 60; + $fixedClock = new FixedClock(); + $handler = new ArraySessionHandler($ttl, $fixedClock); + $handler->write(SESSION_ID_ARRAY_1, SESSION_DATA_1); + + $fixedClock->addSeconds($ttl + 1); + + $readData = $handler->read(SESSION_ID_ARRAY_1); + expect($readData)->toBeFalse(); + + $reflection = new \ReflectionClass($handler); + $storeProp = $reflection->getProperty('store'); + $storeProp->setAccessible(true); + $internalStore = $storeProp->getValue($handler); + expect($internalStore)->not->toHaveKey(SESSION_ID_ARRAY_1); +}); + +it('does not return data if read exactly at TTL expiration time', function () { + $shortTtl = 60; + $fixedClock = new FixedClock(); + $handler = new ArraySessionHandler($shortTtl, $fixedClock); + $handler->write(SESSION_ID_ARRAY_1, SESSION_DATA_1); + + $fixedClock->addSeconds($shortTtl); + + $readData = $handler->read(SESSION_ID_ARRAY_1); + expect($readData)->toBe(SESSION_DATA_1); + + $fixedClock->addSecond(); + + $readDataExpired = $handler->read(SESSION_ID_ARRAY_1); + expect($readDataExpired)->toBeFalse(); +}); + + +it('updates timestamp on write, effectively extending session life', function () { + $veryShortTtl = 5; + $fixedClock = new FixedClock(); + $handler = new ArraySessionHandler($veryShortTtl, $fixedClock); + + $handler->write(SESSION_ID_ARRAY_1, SESSION_DATA_1); + + $fixedClock->addSeconds(3); + + $handler->write(SESSION_ID_ARRAY_1, SESSION_DATA_2); + + $fixedClock->addSeconds(3); + + $readData = $handler->read(SESSION_ID_ARRAY_1); + expect($readData)->toBe(SESSION_DATA_2); +}); + +it('destroys an existing session and it cannot be read', function () { + $this->handler->write(SESSION_ID_ARRAY_1, SESSION_DATA_1); + expect($this->handler->read(SESSION_ID_ARRAY_1))->toBe(SESSION_DATA_1); + + $destroyResult = $this->handler->destroy(SESSION_ID_ARRAY_1); + expect($destroyResult)->toBeTrue(); + expect($this->handler->read(SESSION_ID_ARRAY_1))->toBeFalse(); + + $reflection = new \ReflectionClass($this->handler); + $storeProp = $reflection->getProperty('store'); + $storeProp->setAccessible(true); + expect($storeProp->getValue($this->handler))->not->toHaveKey(SESSION_ID_ARRAY_1); +}); + +it('destroy returns true and does nothing for a non-existent session', function () { + $destroyResult = $this->handler->destroy('non-existent-id'); + expect($destroyResult)->toBeTrue(); +}); + +it('garbage collects only sessions older than maxLifetime', function () { + $gcMaxLifetime = 100; + $handlerTtl = 300; + $fixedClock = new FixedClock(); + $handler = new ArraySessionHandler($handlerTtl, $fixedClock); + + $handler->write(SESSION_ID_ARRAY_1, SESSION_DATA_1); + + $fixedClock->addSeconds(50); + $handler->write(SESSION_ID_ARRAY_2, SESSION_DATA_2); + + $fixedClock->addSeconds(80); + + $deletedSessions = $handler->gc($gcMaxLifetime); + + expect($deletedSessions)->toBeArray()->toEqual([SESSION_ID_ARRAY_1]); + expect($handler->read(SESSION_ID_ARRAY_1))->toBeFalse(); + expect($handler->read(SESSION_ID_ARRAY_2))->toBe(SESSION_DATA_2); +}); + +it('garbage collection respects maxLifetime precisely', function () { + $maxLifetime = 60; + $fixedClock = new FixedClock(); + $handler = new ArraySessionHandler(300, $fixedClock); + + $handler->write(SESSION_ID_ARRAY_1, SESSION_DATA_1); + + $fixedClock->addSeconds($maxLifetime); + $deleted = $handler->gc($maxLifetime); + expect($deleted)->toBeEmpty(); + expect($handler->read(SESSION_ID_ARRAY_1))->toBe(SESSION_DATA_1); + + $fixedClock->addSecond(); + $deleted2 = $handler->gc($maxLifetime); + expect($deleted2)->toEqual([SESSION_ID_ARRAY_1]); + expect($handler->read(SESSION_ID_ARRAY_1))->toBeFalse(); +}); + +it('garbage collection returns empty array if no sessions meet criteria', function () { + $this->handler->write(SESSION_ID_ARRAY_1, SESSION_DATA_1); + $this->handler->write(SESSION_ID_ARRAY_2, SESSION_DATA_2); + + $this->fixedClock->addSeconds(DEFAULT_TTL_ARRAY / 2); + + $deletedSessions = $this->handler->gc(DEFAULT_TTL_ARRAY); + expect($deletedSessions)->toBeArray()->toBeEmpty(); + expect($this->handler->read(SESSION_ID_ARRAY_1))->toBe(SESSION_DATA_1); + expect($this->handler->read(SESSION_ID_ARRAY_2))->toBe(SESSION_DATA_2); +}); + +it('garbage collection correctly handles an empty store', function () { + $deletedSessions = $this->handler->gc(DEFAULT_TTL_ARRAY); + expect($deletedSessions)->toBeArray()->toBeEmpty(); +}); + +it('garbage collection removes multiple expired sessions', function () { + $maxLifetime = 30; + $fixedClock = new FixedClock(); + $handler = new ArraySessionHandler(300, $fixedClock); + + $handler->write(SESSION_ID_ARRAY_1, SESSION_DATA_1); + + $fixedClock->addSeconds(20); + $handler->write(SESSION_ID_ARRAY_2, SESSION_DATA_2); + + $fixedClock->addSeconds(20); + $handler->write(SESSION_ID_ARRAY_3, SESSION_DATA_3); + + $fixedClock->addSeconds(20); + + $deleted = $handler->gc($maxLifetime); + expect($deleted)->toHaveCount(2)->toContain(SESSION_ID_ARRAY_1)->toContain(SESSION_ID_ARRAY_2); + expect($handler->read(SESSION_ID_ARRAY_1))->toBeFalse(); + expect($handler->read(SESSION_ID_ARRAY_2))->toBeFalse(); + expect($handler->read(SESSION_ID_ARRAY_3))->toBe(SESSION_DATA_3); +}); diff --git a/vendor/php-mcp/server/tests/Unit/Session/CacheSessionHandlerTest.php b/vendor/php-mcp/server/tests/Unit/Session/CacheSessionHandlerTest.php new file mode 100644 index 000000000..e5a9507ce --- /dev/null +++ b/vendor/php-mcp/server/tests/Unit/Session/CacheSessionHandlerTest.php @@ -0,0 +1,244 @@ +fixedClock = new FixedClock(); + /** @var MockInterface&CacheInterface $cache */ + $this->cache = Mockery::mock(CacheInterface::class); + + $this->cache->shouldReceive('get')->with(SESSION_INDEX_KEY_CACHE, [])->andReturn([])->byDefault(); + $this->cache->shouldReceive('set')->with(SESSION_INDEX_KEY_CACHE, Mockery::any())->andReturn(true)->byDefault(); + + $this->handler = new CacheSessionHandler($this->cache, DEFAULT_TTL_CACHE, $this->fixedClock); +}); + +it('implements SessionHandlerInterface', function () { + expect($this->handler)->toBeInstanceOf(SessionHandlerInterface::class); +}); + +it('constructs with default TTL and SystemClock if no clock provided', function () { + $cacheMock = Mockery::mock(CacheInterface::class); + $cacheMock->shouldReceive('get')->with(SESSION_INDEX_KEY_CACHE, [])->andReturn([])->byDefault(); + $handler = new CacheSessionHandler($cacheMock); + + expect($handler->ttl)->toBe(DEFAULT_TTL_CACHE); + $reflection = new \ReflectionClass($handler); + $clockProp = $reflection->getProperty('clock'); + $clockProp->setAccessible(true); + expect($clockProp->getValue($handler))->toBeInstanceOf(\PhpMcp\Server\Defaults\SystemClock::class); +}); + +it('constructs with a custom TTL and injected clock', function () { + $customTtl = 7200; + $clock = new FixedClock(); + $cacheMock = Mockery::mock(CacheInterface::class); + $cacheMock->shouldReceive('get')->with(SESSION_INDEX_KEY_CACHE, [])->andReturn([])->byDefault(); + $handler = new CacheSessionHandler($cacheMock, $customTtl, $clock); + expect($handler->ttl)->toBe($customTtl); + + $reflection = new \ReflectionClass($handler); + $clockProp = $reflection->getProperty('clock'); + $clockProp->setAccessible(true); + expect($clockProp->getValue($handler))->toBe($clock); +}); + +it('loads session index from cache on construction', function () { + $initialTimestamp = $this->fixedClock->now()->modify('-100 seconds')->getTimestamp(); + $initialIndex = [SESSION_ID_CACHE_1 => $initialTimestamp]; + + $cacheMock = Mockery::mock(CacheInterface::class); + $cacheMock->shouldReceive('get')->with(SESSION_INDEX_KEY_CACHE, [])->once()->andReturn($initialIndex); + + new CacheSessionHandler($cacheMock, DEFAULT_TTL_CACHE, $this->fixedClock); +}); + +it('reads session data from cache', function () { + $sessionIndex = [SESSION_ID_CACHE_1 => $this->fixedClock->now()->modify('-100 seconds')->getTimestamp()]; + $this->cache->shouldReceive('get')->with(SESSION_INDEX_KEY_CACHE, [])->once()->andReturn($sessionIndex); + $this->cache->shouldReceive('get')->with(SESSION_ID_CACHE_1, false)->once()->andReturn(SESSION_DATA_CACHE_1); + + $handler = new CacheSessionHandler($this->cache, DEFAULT_TTL_CACHE, $this->fixedClock); + $readData = $handler->read(SESSION_ID_CACHE_1); + expect($readData)->toBe(SESSION_DATA_CACHE_1); +}); + +it('returns false when reading non-existent session (cache get returns default)', function () { + $this->cache->shouldReceive('get')->with('non-existent-id', false)->once()->andReturn(false); + $readData = $this->handler->read('non-existent-id'); + expect($readData)->toBeFalse(); +}); + +it('writes session data to cache with correct key and TTL, and updates session index', function () { + $expectedTimestamp = $this->fixedClock->now()->getTimestamp(); // 15:00:00 + + $this->cache->shouldReceive('set') + ->with(SESSION_INDEX_KEY_CACHE, [SESSION_ID_CACHE_1 => $expectedTimestamp]) + ->once()->andReturn(true); + $this->cache->shouldReceive('set') + ->with(SESSION_ID_CACHE_1, SESSION_DATA_CACHE_1) + ->once()->andReturn(true); + + $writeResult = $this->handler->write(SESSION_ID_CACHE_1, SESSION_DATA_CACHE_1); + expect($writeResult)->toBeTrue(); +}); + +it('updates timestamp in session index for existing session on write', function () { + $initialWriteTime = $this->fixedClock->now()->modify('-60 seconds')->getTimestamp(); // 14:59:00 + $this->cache->shouldReceive('get')->with(SESSION_INDEX_KEY_CACHE, [])->andReturn([SESSION_ID_CACHE_1 => $initialWriteTime]); + $handler = new CacheSessionHandler($this->cache, DEFAULT_TTL_CACHE, $this->fixedClock); + + $this->fixedClock->addSeconds(90); + $expectedNewTimestamp = $this->fixedClock->now()->getTimestamp(); + + $this->cache->shouldReceive('set') + ->with(SESSION_INDEX_KEY_CACHE, [SESSION_ID_CACHE_1 => $expectedNewTimestamp]) + ->once()->andReturn(true); + $this->cache->shouldReceive('set') + ->with(SESSION_ID_CACHE_1, SESSION_DATA_CACHE_1) + ->once()->andReturn(true); + + $handler->write(SESSION_ID_CACHE_1, SESSION_DATA_CACHE_1); +}); + +it('returns false if cache set for session data fails', function () { + $this->cache->shouldReceive('set')->with(SESSION_INDEX_KEY_CACHE, Mockery::any())->andReturn(true); + $this->cache->shouldReceive('set')->with(SESSION_ID_CACHE_1, SESSION_DATA_CACHE_1) + ->once()->andReturn(false); + + $writeResult = $this->handler->write(SESSION_ID_CACHE_1, SESSION_DATA_CACHE_1); + expect($writeResult)->toBeFalse(); +}); + +it('destroys session by removing from cache and updating index', function () { + $initialTimestamp = $this->fixedClock->now()->getTimestamp(); + $initialIndex = [SESSION_ID_CACHE_1 => $initialTimestamp, SESSION_ID_CACHE_2 => $initialTimestamp]; + $this->cache->shouldReceive('get')->with(SESSION_INDEX_KEY_CACHE, [])->andReturn($initialIndex); + $handler = new CacheSessionHandler($this->cache, DEFAULT_TTL_CACHE, $this->fixedClock); + + $this->cache->shouldReceive('set') + ->with(SESSION_INDEX_KEY_CACHE, [SESSION_ID_CACHE_2 => $initialTimestamp]) + ->once()->andReturn(true); + $this->cache->shouldReceive('delete')->with(SESSION_ID_CACHE_1)->once()->andReturn(true); + + $handler->destroy(SESSION_ID_CACHE_1); +}); + +it('destroy returns true if session ID not in index (cache delete still called)', function () { + $this->cache->shouldReceive('get')->with(SESSION_INDEX_KEY_CACHE, [])->andReturn([]); // Empty index + $handler = new CacheSessionHandler($this->cache, DEFAULT_TTL_CACHE); + + $this->cache->shouldReceive('set')->with(SESSION_INDEX_KEY_CACHE, [])->once()->andReturn(true); // Index remains empty + $this->cache->shouldReceive('delete')->with('non-existent-id')->once()->andReturn(true); // Cache delete for data + + $destroyResult = $handler->destroy('non-existent-id'); + expect($destroyResult)->toBeTrue(); +}); + +it('destroy returns false if cache delete for session data fails', function () { + $this->cache->shouldReceive('get')->with(SESSION_INDEX_KEY_CACHE, [])->andReturn([SESSION_ID_CACHE_1 => time()]); + $handler = new CacheSessionHandler($this->cache, DEFAULT_TTL_CACHE); + + $this->cache->shouldReceive('set')->with(SESSION_INDEX_KEY_CACHE, Mockery::any())->andReturn(true); // Index update + $this->cache->shouldReceive('delete')->with(SESSION_ID_CACHE_1)->once()->andReturn(false); // Data delete fails + + $destroyResult = $handler->destroy(SESSION_ID_CACHE_1); + expect($destroyResult)->toBeFalse(); +}); + +it('garbage collects only sessions older than maxLifetime from cache and index', function () { + $maxLifetime = 120; + + $initialIndex = [ + SESSION_ID_CACHE_1 => $this->fixedClock->now()->modify('-60 seconds')->getTimestamp(), + SESSION_ID_CACHE_2 => $this->fixedClock->now()->modify("-{$maxLifetime} seconds -10 seconds")->getTimestamp(), + SESSION_ID_CACHE_3 => $this->fixedClock->now()->modify('-1000 seconds')->getTimestamp(), + ]; + $this->cache->shouldReceive('get')->with(SESSION_INDEX_KEY_CACHE, [])->andReturn($initialIndex); + $handler = new CacheSessionHandler($this->cache, DEFAULT_TTL_CACHE, $this->fixedClock); + + $this->cache->shouldReceive('delete')->with(SESSION_ID_CACHE_2)->once()->andReturn(true); + $this->cache->shouldReceive('delete')->with(SESSION_ID_CACHE_3)->once()->andReturn(true); + $this->cache->shouldNotReceive('delete')->with(SESSION_ID_CACHE_1); + + $expectedFinalIndex = [SESSION_ID_CACHE_1 => $initialIndex[SESSION_ID_CACHE_1]]; + $this->cache->shouldReceive('set')->with(SESSION_INDEX_KEY_CACHE, $expectedFinalIndex)->once()->andReturn(true); + + $deletedSessionIds = $handler->gc($maxLifetime); + + expect($deletedSessionIds)->toBeArray()->toHaveCount(2) + ->and($deletedSessionIds)->toContain(SESSION_ID_CACHE_2) + ->and($deletedSessionIds)->toContain(SESSION_ID_CACHE_3); +}); + +it('garbage collection respects maxLifetime precisely for cache handler', function () { + $maxLifetime = 60; + + $sessionTimestamp = $this->fixedClock->now()->modify("-{$maxLifetime} seconds")->getTimestamp(); + $initialIndex = [SESSION_ID_CACHE_1 => $sessionTimestamp]; + $this->cache->shouldReceive('get')->with(SESSION_INDEX_KEY_CACHE, [])->andReturn($initialIndex); + $handler = new CacheSessionHandler($this->cache, DEFAULT_TTL_CACHE, $this->fixedClock); + + $this->cache->shouldNotReceive('delete'); + $this->cache->shouldReceive('set')->with(SESSION_INDEX_KEY_CACHE, $initialIndex)->once()->andReturn(true); + $deleted = $handler->gc($maxLifetime); + expect($deleted)->toBeEmpty(); + + $this->fixedClock->addSeconds(1); + $this->cache->shouldReceive('get')->with(SESSION_INDEX_KEY_CACHE, [])->andReturn($initialIndex); + $handlerAfterTimeAdvance = new CacheSessionHandler($this->cache, DEFAULT_TTL_CACHE, $this->fixedClock); + + $this->cache->shouldReceive('delete')->with(SESSION_ID_CACHE_1)->once()->andReturn(true); + $this->cache->shouldReceive('set')->with(SESSION_INDEX_KEY_CACHE, [])->once()->andReturn(true); + $deleted2 = $handlerAfterTimeAdvance->gc($maxLifetime); + expect($deleted2)->toEqual([SESSION_ID_CACHE_1]); +}); + + +it('garbage collection handles an empty session index', function () { + $this->cache->shouldReceive('get')->with(SESSION_INDEX_KEY_CACHE, [])->andReturn([]); + $handler = new CacheSessionHandler($this->cache, DEFAULT_TTL_CACHE); + + $this->cache->shouldReceive('set')->with(SESSION_INDEX_KEY_CACHE, [])->once()->andReturn(true); + $this->cache->shouldNotReceive('delete'); + + $deletedSessions = $handler->gc(DEFAULT_TTL_CACHE); + expect($deletedSessions)->toBeArray()->toBeEmpty(); +}); + +it('garbage collection continues updating index even if a cache delete fails', function () { + $maxLifetime = 60; + + $initialIndex = [ + 'expired_deleted_ok' => $this->fixedClock->now()->modify("-70 seconds")->getTimestamp(), + 'expired_delete_fails' => $this->fixedClock->now()->modify("-80 seconds")->getTimestamp(), + 'survivor' => $this->fixedClock->now()->modify('-30 seconds')->getTimestamp(), + ]; + $this->cache->shouldReceive('get')->with(SESSION_INDEX_KEY_CACHE, [])->andReturn($initialIndex); + $handler = new CacheSessionHandler($this->cache, DEFAULT_TTL_CACHE, $this->fixedClock); + + $this->cache->shouldReceive('delete')->with('expired_deleted_ok')->once()->andReturn(true); + $this->cache->shouldReceive('delete')->with('expired_delete_fails')->once()->andReturn(false); + + $expectedFinalIndex = ['survivor' => $initialIndex['survivor']]; + $this->cache->shouldReceive('set')->with(SESSION_INDEX_KEY_CACHE, $expectedFinalIndex)->once()->andReturn(true); + + $deletedSessionIds = $handler->gc($maxLifetime); + expect($deletedSessionIds)->toHaveCount(2)->toContain('expired_deleted_ok')->toContain('expired_delete_fails'); +}); diff --git a/vendor/php-mcp/server/tests/Unit/Session/SessionManagerTest.php b/vendor/php-mcp/server/tests/Unit/Session/SessionManagerTest.php new file mode 100644 index 000000000..ff28ebf8e --- /dev/null +++ b/vendor/php-mcp/server/tests/Unit/Session/SessionManagerTest.php @@ -0,0 +1,218 @@ +sessionHandler = Mockery::mock(SessionHandlerInterface::class); + /** @var MockInterface&LoggerInterface $logger */ + $this->logger = Mockery::mock(LoggerInterface::class)->shouldIgnoreMissing(); + $this->loop = Loop::get(); + + $this->sessionManager = new SessionManager( + $this->sessionHandler, + $this->logger, + $this->loop, + DEFAULT_TTL_MGR + ); + + $this->sessionHandler->shouldReceive('read')->with(Mockery::any())->andReturn(false)->byDefault(); + $this->sessionHandler->shouldReceive('write')->with(Mockery::any(), Mockery::any())->andReturn(true)->byDefault(); + $this->sessionHandler->shouldReceive('destroy')->with(Mockery::any())->andReturn(true)->byDefault(); + $this->sessionHandler->shouldReceive('gc')->with(Mockery::any())->andReturn([])->byDefault(); +}); + +it('creates a new session with default hydrated values and saves it', function () { + $this->sessionHandler->shouldReceive('write') + ->with(SESSION_ID_MGR_1, Mockery::on(function ($dataJson) { + $data = json_decode($dataJson, true); + expect($data['initialized'])->toBeFalse(); + expect($data['client_info'])->toBeNull(); + expect($data['protocol_version'])->toBeNull(); + expect($data['subscriptions'])->toEqual([]); + expect($data['message_queue'])->toEqual([]); + expect($data['log_level'])->toBeNull(); + return true; + }))->once()->andReturn(true); + + $sessionCreatedEmitted = false; + $emittedSessionId = null; + $emittedSessionObj = null; + $this->sessionManager->on('session_created', function ($id, $session) use (&$sessionCreatedEmitted, &$emittedSessionId, &$emittedSessionObj) { + $sessionCreatedEmitted = true; + $emittedSessionId = $id; + $emittedSessionObj = $session; + }); + + $session = $this->sessionManager->createSession(SESSION_ID_MGR_1); + + expect($session)->toBeInstanceOf(SessionInterface::class); + expect($session->getId())->toBe(SESSION_ID_MGR_1); + expect($session->get('initialized'))->toBeFalse(); + $this->logger->shouldHaveReceived('info')->with('Session created', ['sessionId' => SESSION_ID_MGR_1]); + expect($sessionCreatedEmitted)->toBeTrue(); + expect($emittedSessionId)->toBe(SESSION_ID_MGR_1); + expect($emittedSessionObj)->toBe($session); +}); + +it('gets an existing session if handler read returns data', function () { + $existingData = ['user_id' => 123, 'initialized' => true]; + $this->sessionHandler->shouldReceive('read')->with(SESSION_ID_MGR_1)->once()->andReturn(json_encode($existingData)); + + $session = $this->sessionManager->getSession(SESSION_ID_MGR_1); + expect($session)->toBeInstanceOf(SessionInterface::class); + expect($session->getId())->toBe(SESSION_ID_MGR_1); + expect($session->get('user_id'))->toBe(123); +}); + +it('returns null from getSession if session does not exist (handler read returns false)', function () { + $this->sessionHandler->shouldReceive('read')->with('non-existent')->once()->andReturn(false); + $session = $this->sessionManager->getSession('non-existent'); + expect($session)->toBeNull(); +}); + +it('returns null from getSession if session data is empty after load', function () { + $this->sessionHandler->shouldReceive('read')->with(SESSION_ID_MGR_1)->once()->andReturn(false); + $session = $this->sessionManager->getSession(SESSION_ID_MGR_1); + expect($session)->toBeNull(); +}); + + +it('deletes a session successfully and emits event', function () { + $this->sessionHandler->shouldReceive('destroy')->with(SESSION_ID_MGR_1)->once()->andReturn(true); + + $sessionDeletedEmitted = false; + $emittedSessionId = null; + $this->sessionManager->on('session_deleted', function ($id) use (&$sessionDeletedEmitted, &$emittedSessionId) { + $sessionDeletedEmitted = true; + $emittedSessionId = $id; + }); + + $success = $this->sessionManager->deleteSession(SESSION_ID_MGR_1); + + expect($success)->toBeTrue(); + $this->logger->shouldHaveReceived('info')->with('Session deleted', ['sessionId' => SESSION_ID_MGR_1]); + expect($sessionDeletedEmitted)->toBeTrue(); + expect($emittedSessionId)->toBe(SESSION_ID_MGR_1); +}); + +it('logs warning and does not emit event if deleteSession fails', function () { + $this->sessionHandler->shouldReceive('destroy')->with(SESSION_ID_MGR_1)->once()->andReturn(false); + $sessionDeletedEmitted = false; + $this->sessionManager->on('session_deleted', function () use (&$sessionDeletedEmitted) { + $sessionDeletedEmitted = true; + }); + + $success = $this->sessionManager->deleteSession(SESSION_ID_MGR_1); + + expect($success)->toBeFalse(); + $this->logger->shouldHaveReceived('warning')->with('Failed to delete session', ['sessionId' => SESSION_ID_MGR_1]); + expect($sessionDeletedEmitted)->toBeFalse(); +}); + +it('queues message for existing session', function () { + $sessionData = ['message_queue' => []]; + $this->sessionHandler->shouldReceive('read')->with(SESSION_ID_MGR_1)->andReturn(json_encode($sessionData)); + $message = '{"id":1}'; + + $this->sessionHandler->shouldReceive('write')->with(SESSION_ID_MGR_1, Mockery::on(function ($dataJson) use ($message) { + $data = json_decode($dataJson, true); + expect($data['message_queue'])->toEqual([$message]); + return true; + }))->once()->andReturn(true); + + $this->sessionManager->queueMessage(SESSION_ID_MGR_1, $message); +}); + +it('does nothing on queueMessage if session does not exist', function () { + $this->sessionHandler->shouldReceive('read')->with('no-such-session')->andReturn(false); + $this->sessionHandler->shouldNotReceive('write'); + $this->sessionManager->queueMessage('no-such-session', '{"id":1}'); +}); + +it('dequeues messages from existing session', function () { + $messages = ['{"id":1}', '{"id":2}']; + $sessionData = ['message_queue' => $messages]; + $this->sessionHandler->shouldReceive('read')->with(SESSION_ID_MGR_1)->andReturn(json_encode($sessionData)); + $this->sessionHandler->shouldReceive('write')->with(SESSION_ID_MGR_1, Mockery::on(function ($dataJson) { + $data = json_decode($dataJson, true); + expect($data['message_queue'])->toEqual([]); + return true; + }))->once()->andReturn(true); + + $dequeued = $this->sessionManager->dequeueMessages(SESSION_ID_MGR_1); + expect($dequeued)->toEqual($messages); +}); + +it('returns empty array from dequeueMessages if session does not exist', function () { + $this->sessionHandler->shouldReceive('read')->with('no-such-session')->andReturn(false); + expect($this->sessionManager->dequeueMessages('no-such-session'))->toBe([]); +}); + +it('checks hasQueuedMessages for existing session', function () { + $this->sessionHandler->shouldReceive('read')->with(SESSION_ID_MGR_1)->andReturn(json_encode(['message_queue' => ['msg']])); + expect($this->sessionManager->hasQueuedMessages(SESSION_ID_MGR_1))->toBeTrue(); + + $this->sessionHandler->shouldReceive('read')->with(SESSION_ID_MGR_2)->andReturn(json_encode(['message_queue' => []])); + expect($this->sessionManager->hasQueuedMessages(SESSION_ID_MGR_2))->toBeFalse(); +}); + +it('returns false from hasQueuedMessages if session does not exist', function () { + $this->sessionHandler->shouldReceive('read')->with('no-such-session')->andReturn(false); + expect($this->sessionManager->hasQueuedMessages('no-such-session'))->toBeFalse(); +}); + +it('can stop GC timer on stopGcTimer ', function () { + $loop = Mockery::mock(LoopInterface::class); + $loop->shouldReceive('addPeriodicTimer')->with(Mockery::any(), Mockery::type('callable'))->once()->andReturn(Mockery::mock(TimerInterface::class)); + $loop->shouldReceive('cancelTimer')->with(Mockery::type(TimerInterface::class))->once(); + + $manager = new SessionManager($this->sessionHandler, $this->logger, $loop); + $manager->startGcTimer(); + $manager->stopGcTimer(); +}); + +it('GC timer callback deletes expired sessions', function () { + $clock = new FixedClock(); + + $sessionHandler = new ArraySessionHandler(60, $clock); + $sessionHandler->write('sess_expired', 'data'); + + // $clock->addSeconds(100); + + $manager = new SessionManager( + $sessionHandler, + $this->logger, + ttl: 30, + gcInterval: 0.01 + ); + + $session = $manager->getSession('sess_expired'); + expect($session)->toBeNull(); +}); + + +it('does not start GC timer if already started', function () { + $this->loop = Mockery::mock(LoopInterface::class); + $this->loop->shouldReceive('addPeriodicTimer')->once()->andReturn(Mockery::mock(TimerInterface::class)); + + $manager = new SessionManager($this->sessionHandler, $this->logger, $this->loop); + $manager->startGcTimer(); +}); diff --git a/vendor/php-mcp/server/tests/Unit/Session/SessionTest.php b/vendor/php-mcp/server/tests/Unit/Session/SessionTest.php new file mode 100644 index 000000000..947f05956 --- /dev/null +++ b/vendor/php-mcp/server/tests/Unit/Session/SessionTest.php @@ -0,0 +1,237 @@ +sessionHandler = Mockery::mock(SessionHandlerInterface::class); + $this->sessionHandler->shouldReceive('read')->with(SESSION_ID_SESS)->once()->andReturn(false)->byDefault(); +}); + +it('implements SessionInterface', function () { + $session = new Session($this->sessionHandler, SESSION_ID_SESS); + expect($session)->toBeInstanceOf(SessionInterface::class); +}); + +// --- Constructor and ID Generation --- +it('uses provided ID if given', function () { + $this->sessionHandler->shouldReceive('read')->with(SESSION_ID_SESS)->once()->andReturn(false); + $session = new Session($this->sessionHandler, SESSION_ID_SESS); + expect($session->getId())->toBe(SESSION_ID_SESS); +}); + +it('generates an ID if none is provided', function () { + $this->sessionHandler->shouldReceive('read')->with(Mockery::type('string'))->once()->andReturn(false); + $session = new Session($this->sessionHandler); + expect($session->getId())->toBeString()->toHaveLength(32); +}); + +it('loads data from handler on construction if session exists', function () { + $initialData = ['foo' => 'bar', 'count' => 5, 'nested' => ['value' => true]]; + $this->sessionHandler->shouldReceive('read')->with(SESSION_ID_SESS)->once()->andReturn(json_encode($initialData)); + + $session = new Session($this->sessionHandler, SESSION_ID_SESS); + expect($session->all())->toEqual($initialData); + expect($session->get('foo'))->toBe('bar'); +}); + +it('initializes with empty data if handler read returns false', function () { + $this->sessionHandler->shouldReceive('read')->with(SESSION_ID_SESS)->once()->andReturn(false); + $session = new Session($this->sessionHandler, SESSION_ID_SESS); + expect($session->all())->toBeEmpty(); +}); + +it('initializes with empty data if handler read returns invalid JSON', function () { + $this->sessionHandler->shouldReceive('read')->with(SESSION_ID_SESS)->once()->andReturn('this is not json'); + $session = new Session($this->sessionHandler, SESSION_ID_SESS); + expect($session->all())->toBeEmpty(); +}); + +it('saves current data to handler', function () { + $this->sessionHandler->shouldReceive('read')->andReturn(false); + $session = new Session($this->sessionHandler, SESSION_ID_SESS); + $session->set('name', 'Alice'); + $session->set('level', 10); + + $expectedSavedData = json_encode(['name' => 'Alice', 'level' => 10]); + $this->sessionHandler->shouldReceive('write')->with(SESSION_ID_SESS, $expectedSavedData)->once()->andReturn(true); + + $session->save(); +}); + +it('sets and gets a top-level attribute', function () { + $this->sessionHandler->shouldReceive('read')->andReturn(false); + $session = new Session($this->sessionHandler, SESSION_ID_SESS); + $session->set('name', 'Bob'); + expect($session->get('name'))->toBe('Bob'); + expect($session->has('name'))->toBeTrue(); +}); + +it('gets default value if attribute does not exist', function () { + $this->sessionHandler->shouldReceive('read')->andReturn(false); + $session = new Session($this->sessionHandler, SESSION_ID_SESS); + expect($session->get('nonexistent', 'default_val'))->toBe('default_val'); + expect($session->has('nonexistent'))->toBeFalse(); +}); + +it('sets and gets nested attributes using dot notation', function () { + $this->sessionHandler->shouldReceive('read')->andReturn(false); + $session = new Session($this->sessionHandler, SESSION_ID_SESS); + $session->set('user.profile.email', 'test@example.com'); + $session->set('user.profile.active', true); + $session->set('user.roles', ['admin', 'editor']); + + expect($session->get('user.profile'))->toEqual(['email' => 'test@example.com', 'active' => true]); + expect($session->get('user.roles'))->toEqual(['admin', 'editor']); + expect($session->has('user.profile.email'))->toBeTrue(); + expect($session->has('user.other_profile.settings'))->toBeFalse(); +}); + +it('set does not overwrite if overwrite is false and key exists', function () { + $this->sessionHandler->shouldReceive('read')->andReturn(false); + $session = new Session($this->sessionHandler, SESSION_ID_SESS); + $session->set('counter', 10); + $session->set('counter', 20, false); + expect($session->get('counter'))->toBe(10); + + $session->set('user.id', 1); + $session->set('user.id', 2, false); + expect($session->get('user.id'))->toBe(1); +}); + +it('set overwrites if overwrite is true (default)', function () { + $this->sessionHandler->shouldReceive('read')->andReturn(false); + $session = new Session($this->sessionHandler, SESSION_ID_SESS); + $session->set('counter', 10); + $session->set('counter', 20); + expect($session->get('counter'))->toBe(20); +}); + + +it('forgets a top-level attribute', function () { + $this->sessionHandler->shouldReceive('read')->andReturn(json_encode(['name' => 'Alice', 'age' => 30])); + $session = new Session($this->sessionHandler, SESSION_ID_SESS); + $session->forget('age'); + expect($session->has('age'))->toBeFalse(); + expect($session->has('name'))->toBeTrue(); + expect($session->all())->toEqual(['name' => 'Alice']); +}); + +it('forgets a nested attribute using dot notation', function () { + $initialData = ['user' => ['profile' => ['email' => 'test@example.com', 'status' => 'active'], 'id' => 1]]; + $this->sessionHandler->shouldReceive('read')->andReturn(json_encode($initialData)); + $session = new Session($this->sessionHandler, SESSION_ID_SESS); + + $session->forget('user.profile.status'); + expect($session->has('user.profile.status'))->toBeFalse(); + expect($session->has('user.profile.email'))->toBeTrue(); + expect($session->get('user.profile'))->toEqual(['email' => 'test@example.com']); + + $session->forget('user.profile'); + expect($session->has('user.profile'))->toBeFalse(); + expect($session->get('user'))->toEqual(['id' => 1]); +}); + +it('forget does nothing if key does not exist', function () { + $this->sessionHandler->shouldReceive('read')->andReturn(json_encode(['name' => 'Test'])); + $session = new Session($this->sessionHandler, SESSION_ID_SESS); + $session->forget('nonexistent'); + $session->forget('another_nonexistent'); + expect($session->all())->toEqual(['name' => 'Test']); +}); + +it('pulls an attribute (gets and forgets)', function () { + $initialData = ['item' => 'important', 'user' => ['token' => 'abc123xyz']]; + $this->sessionHandler->shouldReceive('read')->andReturn(json_encode($initialData)); + $session = new Session($this->sessionHandler, SESSION_ID_SESS); + + $pulledItem = $session->pull('item', 'default'); + expect($pulledItem)->toBe('important'); + expect($session->has('item'))->toBeFalse(); + + $pulledToken = $session->pull('user.token'); + expect($pulledToken)->toBe('abc123xyz'); + expect($session->has('user.token'))->toBeFalse(); + expect($session->has('user'))->toBeTrue(); + + $pulledNonExistent = $session->pull('nonexistent', 'fallback'); + expect($pulledNonExistent)->toBe('fallback'); +}); + +it('clears all session data', function () { + $this->sessionHandler->shouldReceive('read')->andReturn(json_encode(['a' => 1, 'b' => 2])); + $session = new Session($this->sessionHandler, SESSION_ID_SESS); + $session->clear(); + expect($session->all())->toBeEmpty(); +}); + +it('returns all data with all()', function () { + $data = ['a' => 1, 'b' => ['c' => 3]]; + $this->sessionHandler->shouldReceive('read')->andReturn(json_encode($data)); + $session = new Session($this->sessionHandler, SESSION_ID_SESS); + expect($session->all())->toEqual($data); +}); + +it('hydrates session data, merging with defaults and removing id', function () { + $this->sessionHandler->shouldReceive('read')->andReturn(false); + $session = new Session($this->sessionHandler, SESSION_ID_SESS); + $newAttributes = [ + 'client_info' => ['name' => 'TestClient', 'version' => '1.1'], + 'protocol_version' => '2024-custom', + 'user_custom_key' => 'my_value', + 'id' => 'should_be_ignored_on_hydrate' + ]; + $session->hydrate($newAttributes); + + $allData = $session->all(); + expect($allData['initialized'])->toBeFalse(); + expect($allData['client_info'])->toEqual(['name' => 'TestClient', 'version' => '1.1']); + expect($allData['protocol_version'])->toBe('2024-custom'); + expect($allData['message_queue'])->toEqual([]); + expect($allData['log_level'])->toBeNull(); + expect($allData['user_custom_key'])->toBe('my_value'); + expect($allData)->not->toHaveKey('id'); +}); + +it('queues messages correctly', function () { + $this->sessionHandler->shouldReceive('read')->andReturn(false); + $session = new Session($this->sessionHandler, SESSION_ID_SESS); + expect($session->hasQueuedMessages())->toBeFalse(); + + $msg1 = '{"jsonrpc":"2.0","method":"n1"}'; + $msg2 = '{"jsonrpc":"2.0","method":"n2"}'; + $session->queueMessage($msg1); + $session->queueMessage($msg2); + + expect($session->hasQueuedMessages())->toBeTrue(); + expect($session->get('message_queue'))->toEqual([$msg1, $msg2]); +}); + +it('dequeues messages and clears queue', function () { + $this->sessionHandler->shouldReceive('read')->andReturn(false); + $session = new Session($this->sessionHandler, SESSION_ID_SESS); + $msg1 = '{"id":1}'; + $msg2 = '{"id":2}'; + $session->queueMessage($msg1); + $session->queueMessage($msg2); + + $dequeued = $session->dequeueMessages(); + expect($dequeued)->toEqual([$msg1, $msg2]); + expect($session->hasQueuedMessages())->toBeFalse(); + expect($session->get('message_queue', 'not_found'))->toEqual([]); + + expect($session->dequeueMessages())->toEqual([]); +}); + +it('jsonSerializes to all session data', function () { + $data = ['serialize' => 'me', 'nested' => ['ok' => true]]; + $this->sessionHandler->shouldReceive('read')->andReturn(json_encode($data)); + $session = new Session($this->sessionHandler, SESSION_ID_SESS); + expect(json_encode($session))->toBe(json_encode($data)); +}); diff --git a/vendor/php-mcp/server/tests/Unit/Utils/DocBlockParserTest.php b/vendor/php-mcp/server/tests/Unit/Utils/DocBlockParserTest.php new file mode 100644 index 000000000..da36d4509 --- /dev/null +++ b/vendor/php-mcp/server/tests/Unit/Utils/DocBlockParserTest.php @@ -0,0 +1,138 @@ +parser = new DocBlockParser(); +}); + +test('getSummary returns correct summary', function () { + $method = new ReflectionMethod(DocBlockTestFixture::class, 'methodWithSummaryOnly'); + $docComment = $method->getDocComment() ?: null; + $docBlock = $this->parser->parseDocBlock($docComment); + expect($this->parser->getSummary($docBlock))->toBe('Simple summary line.'); + + $method2 = new ReflectionMethod(DocBlockTestFixture::class, 'methodWithSummaryAndDescription'); + $docComment2 = $method2->getDocComment() ?: null; + $docBlock2 = $this->parser->parseDocBlock($docComment2); + expect($this->parser->getSummary($docBlock2))->toBe('Summary line here.'); +}); + +test('getDescription returns correct description', function () { + $method = new ReflectionMethod(DocBlockTestFixture::class, 'methodWithSummaryAndDescription'); + $docComment = $method->getDocComment() ?: null; + $docBlock = $this->parser->parseDocBlock($docComment); + $expectedDesc = "Summary line here.\n\nThis is a longer description spanning\nmultiple lines.\nIt might contain *markdown* or `code`."; + expect($this->parser->getDescription($docBlock))->toBe($expectedDesc); + + $method2 = new ReflectionMethod(DocBlockTestFixture::class, 'methodWithSummaryOnly'); + $docComment2 = $method2->getDocComment() ?: null; + $docBlock2 = $this->parser->parseDocBlock($docComment2); + expect($this->parser->getDescription($docBlock2))->toBe('Simple summary line.'); +}); + +test('getParamTags returns structured param info', function () { + $method = new ReflectionMethod(DocBlockTestFixture::class, 'methodWithParams'); + $docComment = $method->getDocComment() ?: null; + $docBlock = $this->parser->parseDocBlock($docComment); + $params = $this->parser->getParamTags($docBlock); + + expect($params)->toBeArray()->toHaveCount(6); + expect($params)->toHaveKeys(['$param1', '$param2', '$param3', '$param4', '$param5', '$param6']); + + expect($params['$param1'])->toBeInstanceOf(Param::class); + expect($params['$param1']->getVariableName())->toBe('param1'); + expect($this->parser->getParamTypeString($params['$param1']))->toBe('string'); + expect($this->parser->getParamDescription($params['$param1']))->toBe('Description for string param.'); + + expect($params['$param2'])->toBeInstanceOf(Param::class); + expect($params['$param2']->getVariableName())->toBe('param2'); + expect($this->parser->getParamTypeString($params['$param2']))->toBe('int|null'); + expect($this->parser->getParamDescription($params['$param2']))->toBe('Description for nullable int param.'); + + expect($params['$param3'])->toBeInstanceOf(Param::class); + expect($params['$param3']->getVariableName())->toBe('param3'); + expect($this->parser->getParamTypeString($params['$param3']))->toBe('bool'); + expect($this->parser->getParamDescription($params['$param3']))->toBeNull(); + + expect($params['$param4'])->toBeInstanceOf(Param::class); + expect($params['$param4']->getVariableName())->toBe('param4'); + expect($this->parser->getParamTypeString($params['$param4']))->toBe('mixed'); + expect($this->parser->getParamDescription($params['$param4']))->toBe('Missing type.'); + + expect($params['$param5'])->toBeInstanceOf(Param::class); + expect($params['$param5']->getVariableName())->toBe('param5'); + expect($this->parser->getParamTypeString($params['$param5']))->toBe('array'); + expect($this->parser->getParamDescription($params['$param5']))->toBe('Array description.'); + + expect($params['$param6'])->toBeInstanceOf(Param::class); + expect($params['$param6']->getVariableName())->toBe('param6'); + expect($this->parser->getParamTypeString($params['$param6']))->toBe('stdClass'); + expect($this->parser->getParamDescription($params['$param6']))->toBe('Object param.'); +}); + +test('getReturnTag returns structured return info', function () { + $method = new ReflectionMethod(DocBlockTestFixture::class, 'methodWithReturn'); + $docComment = $method->getDocComment() ?: null; + $docBlock = $this->parser->parseDocBlock($docComment); + $returnTag = $this->parser->getReturnTag($docBlock); + + expect($returnTag)->toBeInstanceOf(Return_::class); + expect($this->parser->getReturnTypeString($returnTag))->toBe('string'); + expect($this->parser->getReturnDescription($returnTag))->toBe('The result of the operation.'); + + $method2 = new ReflectionMethod(DocBlockTestFixture::class, 'methodWithSummaryOnly'); + $docComment2 = $method2->getDocComment() ?: null; + $docBlock2 = $this->parser->parseDocBlock($docComment2); + expect($this->parser->getReturnTag($docBlock2))->toBeNull(); +}); + +test('getTagsByName returns specific tags', function () { + $method = new ReflectionMethod(DocBlockTestFixture::class, 'methodWithMultipleTags'); + $docComment = $method->getDocComment() ?: null; + $docBlock = $this->parser->parseDocBlock($docComment); + + expect($docBlock)->toBeInstanceOf(DocBlock::class); + + $throwsTags = $docBlock->getTagsByName('throws'); + expect($throwsTags)->toBeArray()->toHaveCount(1); + expect($throwsTags[0])->toBeInstanceOf(Throws::class); + expect((string) $throwsTags[0]->getType())->toBe('\\RuntimeException'); + expect($throwsTags[0]->getDescription()->render())->toBe('If processing fails.'); + + $deprecatedTags = $docBlock->getTagsByName('deprecated'); + expect($deprecatedTags)->toBeArray()->toHaveCount(1); + expect($deprecatedTags[0])->toBeInstanceOf(Deprecated::class); + expect($deprecatedTags[0]->getDescription()->render())->toBe('Use newMethod() instead.'); + + $seeTags = $docBlock->getTagsByName('see'); + expect($seeTags)->toBeArray()->toHaveCount(1); + expect($seeTags[0])->toBeInstanceOf(See::class); + expect((string) $seeTags[0]->getReference())->toContain('DocBlockTestFixture::newMethod()'); + + $nonExistentTags = $docBlock->getTagsByName('nosuchtag'); + expect($nonExistentTags)->toBeArray()->toBeEmpty(); +}); + +test('handles method with no docblock gracefully', function () { + $method = new ReflectionMethod(DocBlockTestFixture::class, 'methodWithNoDocBlock'); + $docComment = $method->getDocComment() ?: null; + $docBlock = $this->parser->parseDocBlock($docComment); + + expect($docBlock)->toBeNull(); + + expect($this->parser->getSummary($docBlock))->toBeNull(); + expect($this->parser->getDescription($docBlock))->toBeNull(); + expect($this->parser->getParamTags($docBlock))->toBeArray()->toBeEmpty(); + expect($this->parser->getReturnTag($docBlock))->toBeNull(); +}); diff --git a/vendor/php-mcp/server/tests/Unit/Utils/HandlerResolverTest.php b/vendor/php-mcp/server/tests/Unit/Utils/HandlerResolverTest.php new file mode 100644 index 000000000..dd7adaa79 --- /dev/null +++ b/vendor/php-mcp/server/tests/Unit/Utils/HandlerResolverTest.php @@ -0,0 +1,148 @@ +toBeInstanceOf(ReflectionFunction::class); + expect($resolved->getNumberOfParameters())->toBe(1); + expect($resolved->getReturnType()->getName())->toBe('string'); +}); + +it('resolves valid array handler', function () { + $handler = [ValidHandlerClass::class, 'publicMethod']; + $resolved = HandlerResolver::resolve($handler); + + expect($resolved)->toBeInstanceOf(ReflectionMethod::class); + expect($resolved->getName())->toBe('publicMethod'); + expect($resolved->getDeclaringClass()->getName())->toBe(ValidHandlerClass::class); +}); + +it('resolves valid invokable class string handler', function () { + $handler = ValidInvokableClass::class; + $resolved = HandlerResolver::resolve($handler); + + expect($resolved)->toBeInstanceOf(ReflectionMethod::class); + expect($resolved->getName())->toBe('__invoke'); + expect($resolved->getDeclaringClass()->getName())->toBe(ValidInvokableClass::class); +}); + +it('resolves static methods for manual registration', function () { + $handler = [ValidHandlerClass::class, 'staticMethod']; + $resolved = HandlerResolver::resolve($handler); + + expect($resolved)->toBeInstanceOf(ReflectionMethod::class); + expect($resolved->getName())->toBe('staticMethod'); + expect($resolved->isStatic())->toBeTrue(); +}); + +it('throws for invalid array handler format (count)', function () { + HandlerResolver::resolve([ValidHandlerClass::class]); +})->throws(InvalidArgumentException::class, 'Invalid array handler format. Expected [ClassName::class, \'methodName\'].'); + +it('throws for invalid array handler format (types)', function () { + HandlerResolver::resolve([ValidHandlerClass::class, 123]); +})->throws(InvalidArgumentException::class, 'Invalid array handler format. Expected [ClassName::class, \'methodName\'].'); + +it('throws for non-existent class in array handler', function () { + HandlerResolver::resolve(['NonExistentClass', 'method']); +})->throws(InvalidArgumentException::class, "Handler class 'NonExistentClass' not found"); + +it('throws for non-existent method in array handler', function () { + HandlerResolver::resolve([ValidHandlerClass::class, 'nonExistentMethod']); +})->throws(InvalidArgumentException::class, "Handler method 'nonExistentMethod' not found in class"); + +it('throws for non-existent class in string handler', function () { + HandlerResolver::resolve('NonExistentInvokableClass'); +})->throws(InvalidArgumentException::class, 'Invalid handler format. Expected Closure, [ClassName::class, \'methodName\'] or InvokableClassName::class string.'); + +it('throws for non-invokable class string handler', function () { + HandlerResolver::resolve(NonInvokableClass::class); +})->throws(InvalidArgumentException::class, "Invokable handler class '" . NonInvokableClass::class . "' must have a public '__invoke' method."); + +it('throws for protected method handler', function () { + HandlerResolver::resolve([ValidHandlerClass::class, 'protectedMethod']); +})->throws(InvalidArgumentException::class, 'must be public'); + +it('throws for private method handler', function () { + HandlerResolver::resolve([ValidHandlerClass::class, 'privateMethod']); +})->throws(InvalidArgumentException::class, 'must be public'); + +it('throws for constructor as handler', function () { + HandlerResolver::resolve([ValidHandlerClass::class, '__construct']); +})->throws(InvalidArgumentException::class, 'cannot be a constructor or destructor'); + +it('throws for destructor as handler', function () { + HandlerResolver::resolve([ValidHandlerClass::class, '__destruct']); +})->throws(InvalidArgumentException::class, 'cannot be a constructor or destructor'); + +it('throws for abstract method handler', function () { + HandlerResolver::resolve([AbstractHandlerClass::class, 'abstractMethod']); +})->throws(InvalidArgumentException::class, 'cannot be abstract'); + +// Test different closure types +it('resolves closures with different signatures', function () { + $noParams = function () { + return 'test'; + }; + $withParams = function (int $a, string $b = 'default') { + return $a . $b; + }; + $variadic = function (...$args) { + return $args; + }; + + expect(HandlerResolver::resolve($noParams))->toBeInstanceOf(ReflectionFunction::class); + expect(HandlerResolver::resolve($withParams))->toBeInstanceOf(ReflectionFunction::class); + expect(HandlerResolver::resolve($variadic))->toBeInstanceOf(ReflectionFunction::class); + + expect(HandlerResolver::resolve($noParams)->getNumberOfParameters())->toBe(0); + expect(HandlerResolver::resolve($withParams)->getNumberOfParameters())->toBe(2); + expect(HandlerResolver::resolve($variadic)->isVariadic())->toBeTrue(); +}); + +// Test that we can distinguish between closures and callable arrays +it('distinguishes between closures and callable arrays', function () { + $closure = function () { + return 'closure'; + }; + $array = [ValidHandlerClass::class, 'publicMethod']; + $string = ValidInvokableClass::class; + + expect(HandlerResolver::resolve($closure))->toBeInstanceOf(ReflectionFunction::class); + expect(HandlerResolver::resolve($array))->toBeInstanceOf(ReflectionMethod::class); + expect(HandlerResolver::resolve($string))->toBeInstanceOf(ReflectionMethod::class); +}); diff --git a/vendor/php-mcp/server/tests/Unit/Utils/SchemaValidatorTest.php b/vendor/php-mcp/server/tests/Unit/Utils/SchemaValidatorTest.php new file mode 100644 index 000000000..5ba6769e4 --- /dev/null +++ b/vendor/php-mcp/server/tests/Unit/Utils/SchemaValidatorTest.php @@ -0,0 +1,454 @@ +loggerMock = Mockery::mock(LoggerInterface::class)->shouldIgnoreMissing(); + $this->validator = new SchemaValidator($this->loggerMock); +}); + +// --- Helper Data & Schemas --- +function getSimpleSchema(): array +{ + return [ + 'type' => 'object', + 'properties' => [ + 'name' => ['type' => 'string', 'description' => 'The name'], + 'age' => ['type' => 'integer', 'minimum' => 0], + 'active' => ['type' => 'boolean'], + 'score' => ['type' => 'number'], + 'items' => ['type' => 'array', 'items' => ['type' => 'string']], + 'nullableValue' => ['type' => ['string', 'null']], + 'optionalValue' => ['type' => 'string'], // Not required + ], + 'required' => ['name', 'age', 'active', 'score', 'items', 'nullableValue'], + 'additionalProperties' => false, + ]; +} + +function getValidData(): array +{ + return [ + 'name' => 'Tester', + 'age' => 30, + 'active' => true, + 'score' => 99.5, + 'items' => ['a', 'b'], + 'nullableValue' => null, + 'optionalValue' => 'present', + ]; +} + +// --- Basic Validation Tests --- + +test('valid data passes validation', function () { + $schema = getSimpleSchema(); + $data = getValidData(); + + $errors = $this->validator->validateAgainstJsonSchema($data, $schema); + expect($errors)->toBeEmpty(); +}); + +test('invalid type generates type error', function () { + $schema = getSimpleSchema(); + $data = getValidData(); + $data['age'] = 'thirty'; // Invalid type + + $errors = $this->validator->validateAgainstJsonSchema($data, $schema); + expect($errors)->toHaveCount(1) + ->and($errors[0]['pointer'])->toBe('/age') + ->and($errors[0]['keyword'])->toBe('type') + ->and($errors[0]['message'])->toContain('Expected `integer`'); +}); + +test('missing required property generates required error', function () { + $schema = getSimpleSchema(); + $data = getValidData(); + unset($data['name']); // Missing required + + $errors = $this->validator->validateAgainstJsonSchema($data, $schema); + expect($errors)->toHaveCount(1) + ->and($errors[0]['keyword'])->toBe('required') + ->and($errors[0]['message'])->toContain('Missing required properties: `name`'); +}); + +test('additional property generates additionalProperties error', function () { + $schema = getSimpleSchema(); + $data = getValidData(); + $data['extra'] = 'not allowed'; // Additional property + + $errors = $this->validator->validateAgainstJsonSchema($data, $schema); + expect($errors)->toHaveCount(1) + ->and($errors[0]['pointer'])->toBe('/') // Error reported at the object root + ->and($errors[0]['keyword'])->toBe('additionalProperties') + ->and($errors[0]['message'])->toContain('Additional object properties are not allowed: ["extra"]'); +}); + +// --- Keyword Constraint Tests --- + +test('enum constraint violation', function () { + $schema = ['type' => 'string', 'enum' => ['A', 'B']]; + $data = 'C'; + + $errors = $this->validator->validateAgainstJsonSchema($data, $schema); + expect($errors)->toHaveCount(1) + ->and($errors[0]['keyword'])->toBe('enum') + ->and($errors[0]['message'])->toContain('must be one of the allowed values: "A", "B"'); +}); + +test('minimum constraint violation', function () { + $schema = ['type' => 'integer', 'minimum' => 10]; + $data = 5; + + $errors = $this->validator->validateAgainstJsonSchema($data, $schema); + expect($errors)->toHaveCount(1) + ->and($errors[0]['keyword'])->toBe('minimum') + ->and($errors[0]['message'])->toContain('must be greater than or equal to 10'); +}); + +test('maxLength constraint violation', function () { + $schema = ['type' => 'string', 'maxLength' => 5]; + $data = 'toolong'; + + $errors = $this->validator->validateAgainstJsonSchema($data, $schema); + expect($errors)->toHaveCount(1) + ->and($errors[0]['keyword'])->toBe('maxLength') + ->and($errors[0]['message'])->toContain('Maximum string length is 5, found 7'); +}); + +test('pattern constraint violation', function () { + $schema = ['type' => 'string', 'pattern' => '^[a-z]+$']; + $data = '123'; + + $errors = $this->validator->validateAgainstJsonSchema($data, $schema); + expect($errors)->toHaveCount(1) + ->and($errors[0]['keyword'])->toBe('pattern') + ->and($errors[0]['message'])->toContain('does not match the required pattern: `^[a-z]+$`'); +}); + +test('minItems constraint violation', function () { + $schema = ['type' => 'array', 'minItems' => 2]; + $data = ['one']; + + $errors = $this->validator->validateAgainstJsonSchema($data, $schema); + expect($errors)->toHaveCount(1) + ->and($errors[0]['keyword'])->toBe('minItems') + ->and($errors[0]['message'])->toContain('Array should have at least 2 items, 1 found'); +}); + +test('uniqueItems constraint violation', function () { + $schema = ['type' => 'array', 'uniqueItems' => true]; + $data = ['a', 'b', 'a']; + + $errors = $this->validator->validateAgainstJsonSchema($data, $schema); + expect($errors)->toHaveCount(1) + ->and($errors[0]['keyword'])->toBe('uniqueItems') + ->and($errors[0]['message'])->toContain('Array must have unique items'); +}); + +// --- Nested Structures and Pointers --- +test('nested object validation error pointer', function () { + $schema = [ + 'type' => 'object', + 'properties' => [ + 'user' => [ + 'type' => 'object', + 'properties' => ['id' => ['type' => 'integer']], + 'required' => ['id'], + ], + ], + 'required' => ['user'], + ]; + $data = ['user' => ['id' => 'abc']]; // Invalid nested type + + $errors = $this->validator->validateAgainstJsonSchema($data, $schema); + expect($errors)->toHaveCount(1) + ->and($errors[0]['pointer'])->toBe('/user/id'); +}); + +test('array item validation error pointer', function () { + $schema = [ + 'type' => 'array', + 'items' => ['type' => 'integer'], + ]; + $data = [1, 2, 'three', 4]; // Invalid item type + + $errors = $this->validator->validateAgainstJsonSchema($data, $schema); + expect($errors)->toHaveCount(1) + ->and($errors[0]['pointer'])->toBe('/2'); // Pointer to the index of the invalid item +}); + +// --- Data Conversion Tests --- +test('validates data passed as stdClass object', function () { + $schema = getSimpleSchema(); + $dataObj = json_decode(json_encode(getValidData())); // Convert to stdClass + + $errors = $this->validator->validateAgainstJsonSchema($dataObj, $schema); + expect($errors)->toBeEmpty(); +}); + +test('validates data with nested associative arrays correctly', function () { + $schema = [ + 'type' => 'object', + 'properties' => [ + 'nested' => [ + 'type' => 'object', + 'properties' => ['key' => ['type' => 'string']], + 'required' => ['key'], + ], + ], + 'required' => ['nested'], + ]; + $data = ['nested' => ['key' => 'value']]; // Nested assoc array + + $errors = $this->validator->validateAgainstJsonSchema($data, $schema); + expect($errors)->toBeEmpty(); +}); + +// --- Edge Cases --- +test('handles invalid schema structure gracefully', function () { + $schema = ['type' => 'object', 'properties' => ['name' => ['type' => 123]]]; // Invalid type value + $data = ['name' => 'test']; + + $errors = $this->validator->validateAgainstJsonSchema($data, $schema); + expect($errors)->toHaveCount(1) + ->and($errors[0]['keyword'])->toBe('internal') + ->and($errors[0]['message'])->toContain('Schema validation process failed'); +}); + +test('handles empty data object against schema requiring properties', function () { + $schema = getSimpleSchema(); // Requires name, age etc. + $data = []; // Empty data + + $errors = $this->validator->validateAgainstJsonSchema($data, $schema); + + expect($errors)->not->toBeEmpty() + ->and($errors[0]['keyword'])->toBe('required'); +}); + +test('handles empty schema (allows anything)', function () { + $schema = []; // Empty schema object/array implies no constraints + $data = ['anything' => [1, 2], 'goes' => true]; + + $errors = $this->validator->validateAgainstJsonSchema($data, $schema); + + expect($errors)->not->toBeEmpty() + ->and($errors[0]['keyword'])->toBe('internal') + ->and($errors[0]['message'])->toContain('Invalid schema'); +}); + +test('validates schema with string format constraints from Schema attribute', function () { + $emailSchema = (new Schema(format: 'email'))->toArray(); + + // Valid email + $validErrors = $this->validator->validateAgainstJsonSchema('user@example.com', $emailSchema); + expect($validErrors)->toBeEmpty(); + + // Invalid email + $invalidErrors = $this->validator->validateAgainstJsonSchema('not-an-email', $emailSchema); + expect($invalidErrors)->not->toBeEmpty() + ->and($invalidErrors[0]['keyword'])->toBe('format') + ->and($invalidErrors[0]['message'])->toContain('email'); +}); + +test('validates schema with string length constraints from Schema attribute', function () { + $passwordSchema = (new Schema(minLength: 8, pattern: '^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$'))->toArray(); + + // Valid password (meets length and pattern) + $validErrors = $this->validator->validateAgainstJsonSchema('Password123', $passwordSchema); + expect($validErrors)->toBeEmpty(); + + // Invalid - too short + $shortErrors = $this->validator->validateAgainstJsonSchema('Pass1', $passwordSchema); + expect($shortErrors)->not->toBeEmpty() + ->and($shortErrors[0]['keyword'])->toBe('minLength'); + + // Invalid - no digit + $noDigitErrors = $this->validator->validateAgainstJsonSchema('PasswordXYZ', $passwordSchema); + expect($noDigitErrors)->not->toBeEmpty() + ->and($noDigitErrors[0]['keyword'])->toBe('pattern'); +}); + +test('validates schema with numeric constraints from Schema attribute', function () { + $ageSchema = (new Schema(minimum: 18, maximum: 120))->toArray(); + + // Valid age + $validErrors = $this->validator->validateAgainstJsonSchema(25, $ageSchema); + expect($validErrors)->toBeEmpty(); + + // Invalid - too low + $tooLowErrors = $this->validator->validateAgainstJsonSchema(15, $ageSchema); + expect($tooLowErrors)->not->toBeEmpty() + ->and($tooLowErrors[0]['keyword'])->toBe('minimum'); + + // Invalid - too high + $tooHighErrors = $this->validator->validateAgainstJsonSchema(150, $ageSchema); + expect($tooHighErrors)->not->toBeEmpty() + ->and($tooHighErrors[0]['keyword'])->toBe('maximum'); +}); + +test('validates schema with array constraints from Schema attribute', function () { + $tagsSchema = (new Schema(uniqueItems: true, minItems: 2))->toArray(); + + // Valid tags array + $validErrors = $this->validator->validateAgainstJsonSchema(['php', 'javascript', 'python'], $tagsSchema); + expect($validErrors)->toBeEmpty(); + + // Invalid - duplicate items + $duplicateErrors = $this->validator->validateAgainstJsonSchema(['php', 'php', 'javascript'], $tagsSchema); + expect($duplicateErrors)->not->toBeEmpty() + ->and($duplicateErrors[0]['keyword'])->toBe('uniqueItems'); + + // Invalid - too few items + $tooFewErrors = $this->validator->validateAgainstJsonSchema(['php'], $tagsSchema); + expect($tooFewErrors)->not->toBeEmpty() + ->and($tooFewErrors[0]['keyword'])->toBe('minItems'); +}); + +test('validates schema with object constraints from Schema attribute', function () { + $userSchema = (new Schema( + properties: [ + 'name' => ['type' => 'string', 'minLength' => 2], + 'email' => ['type' => 'string', 'format' => 'email'], + 'age' => ['type' => 'integer', 'minimum' => 18] + ], + required: ['name', 'email'] + ))->toArray(); + + // Valid user object + $validUser = [ + 'name' => 'John', + 'email' => 'john@example.com', + 'age' => 25 + ]; + $validErrors = $this->validator->validateAgainstJsonSchema($validUser, $userSchema); + expect($validErrors)->toBeEmpty(); + + // Invalid - missing required email + $missingEmailUser = [ + 'name' => 'John', + 'age' => 25 + ]; + $missingErrors = $this->validator->validateAgainstJsonSchema($missingEmailUser, $userSchema); + expect($missingErrors)->not->toBeEmpty() + ->and($missingErrors[0]['keyword'])->toBe('required'); + + // Invalid - name too short + $shortNameUser = [ + 'name' => 'J', + 'email' => 'john@example.com', + 'age' => 25 + ]; + $nameErrors = $this->validator->validateAgainstJsonSchema($shortNameUser, $userSchema); + expect($nameErrors)->not->toBeEmpty() + ->and($nameErrors[0]['keyword'])->toBe('minLength'); + + // Invalid - age too low + $youngUser = [ + 'name' => 'John', + 'email' => 'john@example.com', + 'age' => 15 + ]; + $ageErrors = $this->validator->validateAgainstJsonSchema($youngUser, $userSchema); + expect($ageErrors)->not->toBeEmpty() + ->and($ageErrors[0]['keyword'])->toBe('minimum'); +}); + +test('validates schema with nested constraints from Schema attribute', function () { + $orderSchema = (new Schema( + properties: [ + 'customer' => [ + 'type' => 'object', + 'properties' => [ + 'id' => ['type' => 'string', 'pattern' => '^CUS-[0-9]{6}$'], + 'name' => ['type' => 'string', 'minLength' => 2] + ], + ], + 'items' => [ + 'type' => 'array', + 'minItems' => 1, + 'items' => [ + 'type' => 'object', + 'properties' => [ + 'product_id' => ['type' => 'string', 'pattern' => '^PRD-[0-9]{4}$'], + 'quantity' => ['type' => 'integer', 'minimum' => 1] + ], + 'required' => ['product_id', 'quantity'] + ] + ] + ], + required: ['customer', 'items'] + ))->toArray(); + + // Valid order + $validOrder = [ + 'customer' => [ + 'id' => 'CUS-123456', + 'name' => 'John' + ], + 'items' => [ + [ + 'product_id' => 'PRD-1234', + 'quantity' => 2 + ] + ] + ]; + $validErrors = $this->validator->validateAgainstJsonSchema($validOrder, $orderSchema); + expect($validErrors)->toBeEmpty(); + + // Invalid - bad customer ID format + $badCustomerIdOrder = [ + 'customer' => [ + 'id' => 'CUST-123', // Wrong format + 'name' => 'John' + ], + 'items' => [ + [ + 'product_id' => 'PRD-1234', + 'quantity' => 2 + ] + ] + ]; + $customerIdErrors = $this->validator->validateAgainstJsonSchema($badCustomerIdOrder, $orderSchema); + expect($customerIdErrors)->not->toBeEmpty() + ->and($customerIdErrors[0]['keyword'])->toBe('pattern'); + + // Invalid - empty items array + $emptyItemsOrder = [ + 'customer' => [ + 'id' => 'CUS-123456', + 'name' => 'John' + ], + 'items' => [] + ]; + $emptyItemsErrors = $this->validator->validateAgainstJsonSchema($emptyItemsOrder, $orderSchema); + expect($emptyItemsErrors)->not->toBeEmpty() + ->and($emptyItemsErrors[0]['keyword'])->toBe('minItems'); + + // Invalid - missing required property in items + $missingProductIdOrder = [ + 'customer' => [ + 'id' => 'CUS-123456', + 'name' => 'John' + ], + 'items' => [ + [ + // Missing product_id + 'quantity' => 2 + ] + ] + ]; + $missingProductIdErrors = $this->validator->validateAgainstJsonSchema($missingProductIdOrder, $orderSchema); + expect($missingProductIdErrors)->not->toBeEmpty() + ->and($missingProductIdErrors[0]['keyword'])->toBe('required'); +}); diff --git a/vendor/phpdocumentor/reflection-common/.github/dependabot.yml b/vendor/phpdocumentor/reflection-common/.github/dependabot.yml new file mode 100644 index 000000000..c630ffa6b --- /dev/null +++ b/vendor/phpdocumentor/reflection-common/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: +- package-ecosystem: composer + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 diff --git a/vendor/phpdocumentor/reflection-common/.github/workflows/push.yml b/vendor/phpdocumentor/reflection-common/.github/workflows/push.yml new file mode 100644 index 000000000..484410e9a --- /dev/null +++ b/vendor/phpdocumentor/reflection-common/.github/workflows/push.yml @@ -0,0 +1,223 @@ +on: + push: + branches: + - 2.x + pull_request: +name: Qa workflow +jobs: + setup: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Restore/cache vendor folder + uses: actions/cache@v1 + with: + path: vendor + key: all-build-${{ hashFiles('**/composer.lock') }} + restore-keys: | + all-build-${{ hashFiles('**/composer.lock') }} + all-build- + + - name: Restore/cache tools folder + uses: actions/cache@v1 + with: + path: tools + key: all-tools-${{ github.sha }} + restore-keys: | + all-tools-${{ github.sha }}- + all-tools- + + - name: composer + uses: docker://composer + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + args: install --no-interaction --prefer-dist --optimize-autoloader + + - name: Install phive + run: make install-phive + + - name: Install PHAR dependencies + run: tools/phive.phar --no-progress install --copy --trust-gpg-keys 4AA394086372C20A,8A03EA3B385DBAA1 --force-accept-unsigned + + phpunit-with-coverage: + runs-on: ubuntu-latest + name: Unit tests + needs: setup + steps: + - uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 7.2 + ini-values: memory_limit=2G, display_errors=On, error_reporting=-1 + coverage: pcov + + - name: Restore/cache tools folder + uses: actions/cache@v1 + with: + path: tools + key: all-tools-${{ github.sha }} + restore-keys: | + all-tools-${{ github.sha }}- + all-tools- + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v1 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ubuntu-latest-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ubuntu-latest-composer- + + - name: Install Composer dependencies + run: | + composer install --no-progress --no-suggest --prefer-dist --optimize-autoloader + + - name: Run PHPUnit + run: php tools/phpunit + + phpunit: + runs-on: ${{ matrix.operating-system }} + strategy: + matrix: + operating-system: + - ubuntu-latest + - windows-latest + - macOS-latest + php-versions: ['7.2', '7.3', '7.4', '8.0'] + name: Unit tests for PHP version ${{ matrix.php-versions }} on ${{ matrix.operating-system }} + needs: + - setup + - phpunit-with-coverage + steps: + - uses: actions/checkout@v2 + + - name: Restore/cache tools folder + uses: actions/cache@v1 + with: + path: tools + key: all-tools-${{ github.sha }} + restore-keys: | + all-tools-${{ github.sha }}- + all-tools- + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + ini-values: memory_limit=2G, display_errors=On, error_reporting=-1 + coverage: none + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v1 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install Composer dependencies + run: | + composer install --no-progress --no-suggest --prefer-dist --optimize-autoloader + + - name: Run PHPUnit + continue-on-error: true + run: php tools/phpunit + + codestyle: + runs-on: ubuntu-latest + needs: [setup, phpunit] + steps: + - uses: actions/checkout@v2 + - name: Restore/cache vendor folder + uses: actions/cache@v1 + with: + path: vendor + key: all-build-${{ hashFiles('**/composer.lock') }} + restore-keys: | + all-build-${{ hashFiles('**/composer.lock') }} + all-build- + - name: Code style check + uses: phpDocumentor/coding-standard@latest + with: + args: -s + + phpstan: + runs-on: ubuntu-latest + needs: [setup, phpunit] + steps: + - uses: actions/checkout@v2 + - name: Restore/cache vendor folder + uses: actions/cache@v1 + with: + path: vendor + key: all-build-${{ hashFiles('**/composer.lock') }} + restore-keys: | + all-build-${{ hashFiles('**/composer.lock') }} + all-build- + - name: PHPStan + uses: phpDocumentor/phpstan-ga@latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + args: analyse src --configuration phpstan.neon + + psalm: + runs-on: ubuntu-latest + needs: [setup, phpunit] + steps: + - uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 7.2 + ini-values: memory_limit=2G, display_errors=On, error_reporting=-1 + tools: psalm + coverage: none + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v1 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install Composer dependencies + run: | + composer install --no-progress --no-suggest --prefer-dist --optimize-autoloader + + - name: Psalm + run: psalm --output-format=github + + bc_check: + name: BC Check + runs-on: ubuntu-latest + needs: [setup, phpunit] + steps: + - uses: actions/checkout@v2 + - name: fetch tags + run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* + - name: Restore/cache vendor folder + uses: actions/cache@v1 + with: + path: vendor + key: all-build-${{ hashFiles('**/composer.lock') }} + restore-keys: | + all-build-${{ hashFiles('**/composer.lock') }} + all-build- + - name: Roave BC Check + uses: docker://nyholm/roave-bc-check-ga diff --git a/vendor/phpdocumentor/reflection-common/LICENSE b/vendor/phpdocumentor/reflection-common/LICENSE new file mode 100644 index 000000000..ed6926c1e --- /dev/null +++ b/vendor/phpdocumentor/reflection-common/LICENSE @@ -0,0 +1,22 @@ +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. + diff --git a/vendor/phpdocumentor/reflection-common/README.md b/vendor/phpdocumentor/reflection-common/README.md new file mode 100644 index 000000000..70f830dc7 --- /dev/null +++ b/vendor/phpdocumentor/reflection-common/README.md @@ -0,0 +1,11 @@ +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +![Qa workflow](https://github.com/phpDocumentor/ReflectionCommon/workflows/Qa%20workflow/badge.svg) +[![Coveralls Coverage](https://img.shields.io/coveralls/github/phpDocumentor/ReflectionCommon.svg)](https://coveralls.io/github/phpDocumentor/ReflectionCommon?branch=master) +[![Scrutinizer Code Coverage](https://img.shields.io/scrutinizer/coverage/g/phpDocumentor/ReflectionCommon.svg)](https://scrutinizer-ci.com/g/phpDocumentor/ReflectionCommon/?branch=master) +[![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/phpDocumentor/ReflectionCommon.svg)](https://scrutinizer-ci.com/g/phpDocumentor/ReflectionCommon/?branch=master) +[![Stable Version](https://img.shields.io/packagist/v/phpDocumentor/Reflection-Common.svg)](https://packagist.org/packages/phpDocumentor/Reflection-Common) +[![Unstable Version](https://img.shields.io/packagist/vpre/phpDocumentor/Reflection-Common.svg)](https://packagist.org/packages/phpDocumentor/Reflection-Common) + + +ReflectionCommon +================ diff --git a/vendor/phpdocumentor/reflection-common/composer.json b/vendor/phpdocumentor/reflection-common/composer.json new file mode 100644 index 000000000..4d128b49a --- /dev/null +++ b/vendor/phpdocumentor/reflection-common/composer.json @@ -0,0 +1,28 @@ +{ + "name": "phpdocumentor/reflection-common", + "keywords": ["phpdoc", "phpDocumentor", "reflection", "static analysis", "FQSEN"], + "homepage": "http://www.phpdoc.org", + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "license": "MIT", + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "require": { + "php": "^7.2 || ^8.0" + }, + "autoload" : { + "psr-4" : { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "require-dev": { + }, + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + } +} diff --git a/vendor/phpdocumentor/reflection-common/src/Element.php b/vendor/phpdocumentor/reflection-common/src/Element.php new file mode 100644 index 000000000..8923e4fb0 --- /dev/null +++ b/vendor/phpdocumentor/reflection-common/src/Element.php @@ -0,0 +1,30 @@ +fqsen = $fqsen; + + if (isset($matches[2])) { + $this->name = $matches[2]; + } else { + $matches = explode('\\', $fqsen); + $name = end($matches); + assert(is_string($name)); + $this->name = trim($name, '()'); + } + } + + /** + * converts this class to string. + */ + public function __toString() : string + { + return $this->fqsen; + } + + /** + * Returns the name of the element without path. + */ + public function getName() : string + { + return $this->name; + } +} diff --git a/vendor/phpdocumentor/reflection-common/src/Location.php b/vendor/phpdocumentor/reflection-common/src/Location.php new file mode 100644 index 000000000..177deede6 --- /dev/null +++ b/vendor/phpdocumentor/reflection-common/src/Location.php @@ -0,0 +1,53 @@ +lineNumber = $lineNumber; + $this->columnNumber = $columnNumber; + } + + /** + * Returns the line number that is covered by this location. + */ + public function getLineNumber() : int + { + return $this->lineNumber; + } + + /** + * Returns the column number (character position on a line) for this location object. + */ + public function getColumnNumber() : int + { + return $this->columnNumber; + } +} diff --git a/vendor/phpdocumentor/reflection-common/src/Project.php b/vendor/phpdocumentor/reflection-common/src/Project.php new file mode 100644 index 000000000..57839fd14 --- /dev/null +++ b/vendor/phpdocumentor/reflection-common/src/Project.php @@ -0,0 +1,25 @@ +create($docComment); +``` + +The `create` method will yield an object of type `\phpDocumentor\Reflection\DocBlock` +whose methods can be queried: + +```php +// Contains the summary for this DocBlock +$summary = $docblock->getSummary(); + +// Contains \phpDocumentor\Reflection\DocBlock\Description object +$description = $docblock->getDescription(); + +// You can either cast it to string +$description = (string) $docblock->getDescription(); + +// Or use the render method to get a string representation of the Description. +$description = $docblock->getDescription()->render(); +``` + +> For more examples it would be best to review the scripts in the [`/examples` folder](/examples). diff --git a/vendor/phpdocumentor/reflection-docblock/composer.json b/vendor/phpdocumentor/reflection-docblock/composer.json new file mode 100644 index 000000000..8105049bd --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/composer.json @@ -0,0 +1,57 @@ +{ + "name": "phpdocumentor/reflection-docblock", + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "require": { + "php": "^7.4 || ^8.0", + "phpdocumentor/type-resolver": "^1.7", + "webmozart/assert": "^1.9.1", + "phpdocumentor/reflection-common": "^2.2", + "ext-filter": "*", + "phpstan/phpdoc-parser": "^1.7|^2.0", + "doctrine/deprecations": "^1.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.5 || ~1.6.0", + "phpunit/phpunit": "^9.5", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "psalm/phar": "^5.26" + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "phpDocumentor\\Reflection\\": ["tests/unit", "tests/integration"] + } + }, + "config": { + "platform": { + "php":"7.4.0" + }, + "allow-plugins": { + "phpstan/extension-installer": true + } + }, + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock.php new file mode 100644 index 000000000..90d8066d5 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock.php @@ -0,0 +1,228 @@ +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; + } + + public function getSummary(): string + { + return $this->summary; + } + + public function getDescription(): DocBlock\Description + { + return $this->description; + } + + /** + * Returns the current context. + */ + public function getContext(): ?Types\Context + { + return $this->context; + } + + /** + * Returns the current location. + */ + public function getLocation(): ?Location + { + 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. + */ + public function isTemplateStart(): bool + { + 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. + */ + public function isTemplateEnd(): bool + { + return $this->isTemplateEnd; + } + + /** + * Returns the tags for this DocBlock. + * + * @return Tag[] + */ + public function getTags(): array + { + 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(string $name): array + { + $result = []; + + foreach ($this->getTags() as $tag) { + if ($tag->getName() !== $name) { + continue; + } + + $result[] = $tag; + } + + return $result; + } + + /** + * Returns an array of tags with type matching the given name. If no tags are found + * an empty array is returned. + * + * @param string $name String to search by. + * + * @return TagWithType[] + */ + public function getTagsWithTypeByName(string $name): array + { + $result = []; + + foreach ($this->getTagsByName($name) as $tag) { + if (!$tag instanceof TagWithType) { + 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. + */ + public function hasTag(string $name): bool + { + foreach ($this->getTags() as $tag) { + if ($tag->getName() === $name) { + return true; + } + } + + return false; + } + + /** + * Remove a tag from this DocBlock. + * + * @param Tag $tagToRemove The tag to remove. + */ + public function removeTag(Tag $tagToRemove): void + { + 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. + */ + private function addTag(Tag $tag): void + { + $this->tags[] = $tag; + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Description.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Description.php new file mode 100644 index 000000000..a188ae30f --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Description.php @@ -0,0 +1,118 @@ +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 Tags\Formatter for the formatting of the body and tags. + */ +class Description +{ + private string $bodyTemplate; + + /** @var Tag[] */ + private array $tags; + + /** + * Initializes a Description with its body (template) and a listing of the tags used in the body template. + * + * @param Tag[] $tags + */ + public function __construct(string $bodyTemplate, array $tags = []) + { + $this->bodyTemplate = $bodyTemplate; + $this->tags = $tags; + } + + /** + * Returns the body template. + */ + public function getBodyTemplate(): string + { + return $this->bodyTemplate; + } + + /** + * Returns the tags for this DocBlock. + * + * @return Tag[] + */ + public function getTags(): array + { + return $this->tags; + } + + /** + * Renders this description as a string where the provided formatter will format the tags in the expected string + * format. + */ + public function render(?Formatter $formatter = null): string + { + if ($this->tags === []) { + return vsprintf($this->bodyTemplate, []); + } + + 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. + */ + public function __toString(): string + { + return $this->render(); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/DescriptionFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/DescriptionFactory.php new file mode 100644 index 000000000..e7bc616d1 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/DescriptionFactory.php @@ -0,0 +1,178 @@ +tagFactory = $tagFactory; + } + + /** + * Returns the parsed text of this description. + */ + public function create(string $contents, ?TypeContext $context = null): Description + { + $tokens = $this->lex($contents); + $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 new Description(implode('', $tokens), $tags); + } + + /** + * Strips the contents from superfluous whitespace and splits the description into a series of tokens. + * + * @return string[] A series of tokens of which the description text is composed. + */ + private function lex(string $contents): array + { + $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 Utils::pregSplit( + '/\{ + # "{@}" 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, + 0, + PREG_SPLIT_DELIM_CAPTURE + ); + } + + /** + * 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. + */ + private function removeSuperfluousStartingWhitespace(string $contents): string + { + $lines = Utils::pregSplit("/\r\n?|\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, $iMax = count($lines); $i < $iMax; ++$i) { + // lines with a no length do not count as they are not indented at all + if (trim($lines[$i]) === '') { + 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, $iMax = count($lines); $i < $iMax; ++$i) { + $lines[$i] = substr($lines[$i], $startingSpaceCount); + } + } + + return implode("\n", $lines); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/ExampleFinder.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/ExampleFinder.php new file mode 100644 index 000000000..0fb24a68b --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/ExampleFinder.php @@ -0,0 +1,158 @@ +getFilePath(); + + $file = $this->getExampleFileContents($filename); + if ($file === null) { + return sprintf('** File not found : %s **', $filename); + } + + return implode('', array_slice($file, $example->getStartingLine() - 1, $example->getLineCount())); + } + + /** + * Registers the project's root directory where an 'examples' folder can be expected. + */ + public function setSourceDirectory(string $directory = ''): void + { + $this->sourceDirectory = $directory; + } + + /** + * Returns the project's root directory where an 'examples' folder can be expected. + */ + public function getSourceDirectory(): string + { + return $this->sourceDirectory; + } + + /** + * Registers a series of directories that may contain examples. + * + * @param string[] $directories + */ + public function setExampleDirectories(array $directories): void + { + $this->exampleDirectories = $directories; + } + + /** + * Returns a series of directories that may contain examples. + * + * @return string[] + */ + public function getExampleDirectories(): array + { + 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 + * + * @return string[] all lines of the example file + */ + private function getExampleFileContents(string $filename): ?array + { + $normalizedPath = null; + + foreach ($this->exampleDirectories as $directory) { + $exampleFileFromConfig = $this->constructExamplePath($directory, $filename); + if (is_readable($exampleFileFromConfig)) { + $normalizedPath = $exampleFileFromConfig; + break; + } + } + + if ($normalizedPath === null) { + 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; + } + } + + $lines = $normalizedPath !== null && is_readable($normalizedPath) ? file($normalizedPath) : false; + + return $lines !== false ? $lines : null; + } + + /** + * Get example filepath based on the example directory inside your project. + */ + private function getExamplePathFromExampleDirectory(string $file): string + { + return getcwd() . DIRECTORY_SEPARATOR . 'examples' . DIRECTORY_SEPARATOR . $file; + } + + /** + * Returns a path to the example file in the given directory.. + */ + private function constructExamplePath(string $directory, string $file): string + { + return rtrim($directory, '\\/') . DIRECTORY_SEPARATOR . $file; + } + + /** + * Get example filepath based on sourcecode. + */ + private function getExamplePathFromSource(string $file): string + { + return sprintf( + '%s%s%s', + trim($this->getSourceDirectory(), '\\/'), + DIRECTORY_SEPARATOR, + trim($file, '"') + ); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Serializer.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Serializer.php new file mode 100644 index 000000000..2c257dd50 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Serializer.php @@ -0,0 +1,156 @@ +indent = $indent; + $this->indentString = $indentString; + $this->isFirstLineIndented = $indentFirstLine; + $this->lineLength = $lineLength; + $this->tagFormatter = $tagFormatter ?: new PassthroughFormatter(); + $this->lineEnding = $lineEnding; + } + + /** + * Generate a DocBlock comment. + * + * @param DocBlock $docblock The DocBlock to serialize. + * + * @return string The serialized doc block. + */ + public function getDocComment(DocBlock $docblock): string + { + $indent = str_repeat($this->indentString, $this->indent); + $firstIndent = $this->isFirstLineIndented ? $indent : ''; + // 3 === strlen(' * ') + $wrapLength = $this->lineLength !== null ? $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); + + return str_replace("\n", $this->lineEnding, $comment . $indent . ' */'); + } + + private function removeTrailingSpaces(string $indent, string $text): string + { + return str_replace( + sprintf("\n%s * \n", $indent), + sprintf("\n%s *\n", $indent), + $text + ); + } + + private function addAsterisksForEachLine(string $indent, string $text): string + { + return str_replace( + "\n", + sprintf("\n%s * ", $indent), + $text + ); + } + + private function getSummaryAndDescriptionTextBlock(DocBlock $docblock, ?int $wrapLength): string + { + $text = $docblock->getSummary() . ((string) $docblock->getDescription() ? "\n\n" . $docblock->getDescription() + : ''); + if ($wrapLength !== null) { + $text = wordwrap($text, $wrapLength); + + return $text; + } + + return $text; + } + + private function addTagBlock(DocBlock $docblock, ?int $wrapLength, string $indent, string $comment): string + { + foreach ($docblock->getTags() as $tag) { + $tagText = $this->tagFormatter->format($tag); + if ($wrapLength !== null) { + $tagText = wordwrap($tagText, $wrapLength); + } + + $tagText = str_replace( + "\n", + sprintf("\n%s * ", $indent), + $tagText + ); + + $comment .= sprintf("%s * %s\n", $indent, $tagText); + } + + return $comment; + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/StandardTagFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/StandardTagFactory.php new file mode 100644 index 000000000..d029cd1b6 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/StandardTagFactory.php @@ -0,0 +1,363 @@ + 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. */ + public const REGEX_TAGNAME = '[\w\-\_\\\\:]+'; + + /** + * @var array|Factory> An array with a tag as a key, and an + * FQCN to a class that handles it as an array value. + */ + private array $tagHandlerMappings = [ + 'author' => Author::class, + 'covers' => Covers::class, + 'deprecated' => Deprecated::class, + // 'example' => '\phpDocumentor\Reflection\DocBlock\Tags\Example', + 'link' => LinkTag::class, + 'mixin' => Mixin::class, + 'method' => Method::class, + 'param' => Param::class, + 'property-read' => PropertyRead::class, + 'property' => Property::class, + 'property-write' => PropertyWrite::class, + 'return' => Return_::class, + 'see' => SeeTag::class, + 'since' => Since::class, + 'source' => Source::class, + 'template-covariant' => TemplateCovariant::class, + 'throw' => Throws::class, + 'throws' => Throws::class, + 'uses' => Uses::class, + 'var' => Var_::class, + 'version' => Version::class, + ]; + + /** + * @var array> An array with an annotation as a key, and an + * FQCN to a class that handles it as an array value. + */ + private array $annotationMappings = []; + + /** + * @var ReflectionParameter[][] a lazy-loading cache containing parameters + * for each tagHandler that has been used. + */ + private array $tagHandlerParameterCache = []; + + private FqsenResolver $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 array $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. + * + * @see self::registerTagHandler() to add a new tag handler to the existing default list. + * + * @param array> $tagHandlers + */ + public function __construct(FqsenResolver $fqsenResolver, ?array $tagHandlers = null) + { + $this->fqsenResolver = $fqsenResolver; + if ($tagHandlers !== null) { + $this->tagHandlerMappings = $tagHandlers; + } + + $this->addService($fqsenResolver, FqsenResolver::class); + } + + public function create(string $tagLine, ?TypeContext $context = null): Tag + { + if (!$context) { + $context = new TypeContext(''); + } + + [$tagName, $tagBody] = $this->extractTagParts($tagLine); + + return $this->createTag(trim($tagBody), $tagName, $context); + } + + /** + * @param mixed $value + */ + public function addParameter(string $name, $value): void + { + $this->serviceLocator[$name] = $value; + } + + public function addService(object $service, ?string $alias = null): void + { + $this->serviceLocator[$alias ?? get_class($service)] = $service; + } + + /** {@inheritDoc} */ + public function registerTagHandler(string $tagName, $handler): void + { + Assert::stringNotEmpty($tagName); + if (strpos($tagName, '\\') !== false && $tagName[0] !== '\\') { + throw new InvalidArgumentException( + 'A namespaced tag must have a leading backslash as it must be fully qualified' + ); + } + + if (is_object($handler)) { + Assert::isInstanceOf($handler, Factory::class); + $this->tagHandlerMappings[$tagName] = $handler; + + return; + } + + Assert::classExists($handler); + Assert::implementsInterface($handler, Tag::class); + $this->tagHandlerMappings[$tagName] = $handler; + } + + /** + * Extracts all components for a tag. + * + * @return string[] + */ + private function extractTagParts(string $tagLine): array + { + $matches = []; + if (!preg_match('/^@(' . self::REGEX_TAGNAME . ')((?:[\s\(\{])\s*([^\s].*)|$)/us', $tagLine, $matches)) { + throw new InvalidArgumentException( + 'The tag "' . $tagLine . '" does not seem to be wellformed, please check it for errors' + ); + } + + 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. + */ + private function createTag(string $body, string $name, TypeContext $context): Tag + { + $handlerClassName = $this->findHandlerClassName($name, $context); + $arguments = $this->getArgumentsForParametersFromWiring( + $this->fetchParametersForHandlerFactoryMethod($handlerClassName), + $this->getServiceLocatorWithDynamicParameters($context, $name, $body) + ); + + if (array_key_exists('tagLine', $arguments)) { + $arguments['tagLine'] = sprintf('@%s %s', $name, $body); + } + + try { + $callable = [$handlerClassName, 'create']; + Assert::isCallable($callable); + /** @phpstan-var callable(string): ?Tag $callable */ + $tag = call_user_func_array($callable, $arguments); + + return $tag ?? InvalidTag::create($body, $name); + } catch (InvalidArgumentException $e) { + return InvalidTag::create($body, $name)->withError($e); + } + } + + /** + * Determines the Fully Qualified Class Name of the Factory or Tag (containing a Factory Method `create`). + * + * @return class-string|Factory + */ + private function findHandlerClassName(string $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(array $parameters, array $locator): array + { + $arguments = []; + foreach ($parameters as $parameter) { + $type = $parameter->getType(); + $typeHint = null; + if ($type instanceof ReflectionNamedType) { + $typeHint = $type->getName(); + if ($typeHint === 'self') { + $declaringClass = $parameter->getDeclaringClass(); + if ($declaringClass !== null) { + $typeHint = $declaringClass->getName(); + } + } + } + + $parameterName = $parameter->getName(); + if (isset($locator[$typeHint])) { + $arguments[$parameterName] = $locator[$typeHint]; + continue; + } + + if (isset($locator[$parameterName])) { + $arguments[$parameterName] = $locator[$parameterName]; + continue; + } + + $arguments[$parameterName] = null; + } + + return $arguments; + } + + /** + * Retrieves a series of ReflectionParameter objects for the static 'create' method of the given + * tag handler class name. + * + * @param class-string|Factory $handler + * + * @return ReflectionParameter[] + */ + private function fetchParametersForHandlerFactoryMethod($handler): array + { + $handlerClassName = is_object($handler) ? get_class($handler) : $handler; + + 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 aliases) 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, + string $tagName, + string $tagBody + ): array { + return array_merge( + $this->serviceLocator, + [ + 'name' => $tagName, + 'body' => $tagBody, + TypeContext::class => $context, + ] + ); + } + + /** + * Returns whether the given tag belongs to an annotation. + * + * @todo this method should be populated once we implement Annotation notation support. + */ + private function isAnnotation(string $tagContent): bool + { + // 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; + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tag.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tag.php new file mode 100644 index 000000000..7cf07b4dd --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tag.php @@ -0,0 +1,31 @@ +|Factory $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. + */ + public function registerTagHandler(string $tagName, $handler): void; +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Author.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Author.php new file mode 100644 index 000000000..290e5a957 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Author.php @@ -0,0 +1,102 @@ +authorName = $authorName; + $this->authorEmail = $authorEmail; + } + + /** + * Gets the author's name. + * + * @return string The author's name. + */ + public function getAuthorName(): string + { + return $this->authorName; + } + + /** + * Returns the author's email. + * + * @return string The author's email. + */ + public function getEmail(): string + { + return $this->authorEmail; + } + + /** + * Returns this tag in string form. + */ + public function __toString(): string + { + if ($this->authorEmail) { + $authorEmail = '<' . $this->authorEmail . '>'; + } else { + $authorEmail = ''; + } + + $authorName = $this->authorName; + + return $authorName . ($authorEmail !== '' ? ($authorName !== '' ? ' ' : '') . $authorEmail : ''); + } + + /** + * Attempts to create a new Author object based on the tag body. + */ + public static function create(string $body): ?self + { + $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); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/BaseTag.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/BaseTag.php new file mode 100644 index 000000000..98b0d881d --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/BaseTag.php @@ -0,0 +1,53 @@ +name; + } + + public function getDescription(): ?Description + { + return $this->description; + } + + public function render(?Formatter $formatter = null): string + { + if ($formatter === null) { + $formatter = new Formatter\PassthroughFormatter(); + } + + return $formatter->format($this); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Covers.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Covers.php new file mode 100644 index 000000000..022594e20 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Covers.php @@ -0,0 +1,99 @@ +refers = $refers; + $this->description = $description; + } + + public static function create( + string $body, + ?DescriptionFactory $descriptionFactory = null, + ?FqsenResolver $resolver = null, + ?TypeContext $context = null + ): self { + Assert::stringNotEmpty($body); + Assert::notNull($descriptionFactory); + Assert::notNull($resolver); + + $parts = Utils::pregSplit('/\s+/Su', $body, 2); + + return new static( + self::resolveFqsen($parts[0], $resolver, $context), + $descriptionFactory->create($parts[1] ?? '', $context) + ); + } + + private static function resolveFqsen(string $parts, ?FqsenResolver $fqsenResolver, ?TypeContext $context): Fqsen + { + Assert::notNull($fqsenResolver); + $fqsenParts = explode('::', $parts); + $resolved = $fqsenResolver->resolve($fqsenParts[0], $context); + + if (!array_key_exists(1, $fqsenParts)) { + return $resolved; + } + + return new Fqsen($resolved . '::' . $fqsenParts[1]); + } + + /** + * Returns the structural element this tag refers to. + */ + public function getReference(): Fqsen + { + return $this->refers; + } + + /** + * Returns a string representation of this tag. + */ + public function __toString(): string + { + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + + $refers = (string) $this->refers; + + return $refers . ($description !== '' ? ($refers !== '' ? ' ' : '') . $description : ''); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Deprecated.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Deprecated.php new file mode 100644 index 000000000..fff591f9d --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Deprecated.php @@ -0,0 +1,108 @@ +version = $version; + $this->description = $description; + } + + /** + * @return static + */ + public static function create( + ?string $body, + ?DescriptionFactory $descriptionFactory = null, + ?TypeContext $context = null + ): self { + if ($body === null || $body === '') { + return new static(); + } + + $matches = []; + if (!preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $body, $matches)) { + return new static( + null, + $descriptionFactory !== null ? $descriptionFactory->create($body, $context) : null + ); + } + + Assert::notNull($descriptionFactory); + + return new static( + $matches[1], + $descriptionFactory->create($matches[2] ?? '', $context) + ); + } + + /** + * Gets the version section of the tag. + */ + public function getVersion(): ?string + { + return $this->version; + } + + /** + * Returns a string representation for this tag. + */ + public function __toString(): string + { + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + + $version = (string) $this->version; + + return $version . ($description !== '' ? ($version !== '' ? ' ' : '') . $description : ''); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Example.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Example.php new file mode 100644 index 000000000..3ddbb686c --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Example.php @@ -0,0 +1,197 @@ +filePath = $filePath; + $this->startingLine = $startingLine; + $this->lineCount = $lineCount; + if ($content !== null) { + $this->content = trim($content); + } + + $this->isURI = $isURI; + } + + public function getContent(): string + { + if ($this->content === null || $this->content === '') { + $filePath = $this->filePath; + if ($this->isURI) { + $filePath = $this->isUriRelative($this->filePath) + ? str_replace('%2F', '/', rawurlencode($this->filePath)) + : $this->filePath; + } + + return trim($filePath); + } + + return $this->content; + } + + public function getDescription(): ?string + { + return $this->content; + } + + public static function create(string $body): ?Tag + { + // File component: File path in quotes or File URI / Source information + if (!preg_match('/^\s*(?:(\"[^\"]+\")|(\S+))(?:\s+(.*))?$/sux', $body, $matches)) { + return null; + } + + $filePath = null; + $fileUri = null; + if (array_key_exists(1, $matches) && $matches[1] !== '') { + $filePath = $matches[1]; + } else { + $fileUri = array_key_exists(2, $matches) ? $matches[2] : ''; + } + + $startingLine = 1; + $lineCount = 0; + $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])) { + $lineCount = (int) $contentMatches[2]; + } + + if (array_key_exists(3, $contentMatches)) { + $description = $contentMatches[3]; + } + } + } + + return new static( + $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(): string + { + return trim($this->filePath, '"'); + } + + /** + * Returns a string representation for this tag. + */ + public function __toString(): string + { + $filePath = $this->filePath; + $isDefaultLine = $this->startingLine === 1 && $this->lineCount === 0; + $startingLine = !$isDefaultLine ? (string) $this->startingLine : ''; + $lineCount = !$isDefaultLine ? (string) $this->lineCount : ''; + $content = (string) $this->content; + + return $filePath + . ($startingLine !== '' + ? ($filePath !== '' ? ' ' : '') . $startingLine + : '') + . ($lineCount !== '' + ? ($filePath !== '' || $startingLine !== '' ? ' ' : '') . $lineCount + : '') + . ($content !== '' + ? ($filePath !== '' || $startingLine !== '' || $lineCount !== '' ? ' ' : '') . $content + : ''); + } + + /** + * Returns true if the provided URI is relative or contains a complete scheme (and thus is absolute). + */ + private function isUriRelative(string $uri): bool + { + return strpos($uri, ':') === false; + } + + public function getStartingLine(): int + { + return $this->startingLine; + } + + public function getLineCount(): int + { + return $this->lineCount; + } + + public function getName(): string + { + return 'example'; + } + + public function render(?Formatter $formatter = null): string + { + if ($formatter === null) { + $formatter = new Formatter\PassthroughFormatter(); + } + + return $formatter->format($this); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Extends_.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Extends_.php new file mode 100644 index 000000000..831af03d6 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Extends_.php @@ -0,0 +1,48 @@ +name = 'extends'; + $this->type = $type; + $this->description = $description; + } + + /** + * @deprecated Create using static factory is deprecated, + * this method should not be called directly by library consumers + */ + public static function create(string $body): ?Tag + { + Deprecation::trigger( + 'phpdocumentor/reflection-docblock', + 'https://github.com/phpDocumentor/ReflectionDocBlock/issues/361', + 'Create using static factory is deprecated, this method should not be called directly + by library consumers', + ); + + return null; + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/AbstractPHPStanFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/AbstractPHPStanFactory.php new file mode 100644 index 000000000..f8bababfb --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/AbstractPHPStanFactory.php @@ -0,0 +1,136 @@ + true, 'lines' => true]); + $this->lexer = new Lexer($config); + $constParser = new ConstExprParser($config); + $this->parser = new PhpDocParser( + $config, + new TypeParser($config, $constParser), + $constParser + ); + } else { + $this->lexer = new Lexer(true); + $constParser = new ConstExprParser(true, true, ['lines' => true, 'indexes' => true]); + $this->parser = new PhpDocParser( + new TypeParser($constParser, true, ['lines' => true, 'indexes' => true]), + $constParser, + true, + true, + ['lines' => true, 'indexes' => true], + true + ); + } + + $this->factories = $factories; + } + + public function create(string $tagLine, ?TypeContext $context = null): Tag + { + $tokens = $this->tokenizeLine($tagLine . "\n"); + $ast = $this->parser->parseTag($tokens); + if (property_exists($ast->value, 'description') === true) { + $ast->value->setAttribute( + 'description', + rtrim($ast->value->description . $tokens->joinUntil(Lexer::TOKEN_END), "\n") + ); + } + + if ($context === null) { + $context = new TypeContext(''); + } + + try { + foreach ($this->factories as $factory) { + if ($factory->supports($ast, $context)) { + return $factory->create($ast, $context); + } + } + } catch (RuntimeException $e) { + return InvalidTag::create((string) $ast->value, 'method')->withError($e); + } + + return InvalidTag::create( + (string) $ast->value, + $ast->name + ); + } + + /** + * Solve the issue with the lexer not tokenizing the line correctly + * + * This method is a workaround for the lexer that includes newline tokens with spaces. For + * phpstan this isn't an issue, as it doesn't do a lot of things with the indentation of descriptions. + * But for us is important to keep the indentation of the descriptions, so we need to fix the lexer output. + */ + private function tokenizeLine(string $tagLine): TokenIterator + { + $tokens = $this->lexer->tokenize($tagLine); + $fixed = []; + foreach ($tokens as $token) { + if (($token[1] === Lexer::TOKEN_PHPDOC_EOL) && rtrim($token[0], " \t") !== $token[0]) { + $fixed[] = [ + rtrim($token[Lexer::VALUE_OFFSET], " \t"), + Lexer::TOKEN_PHPDOC_EOL, + $token[2] ?? 0, + ]; + $fixed[] = [ + ltrim($token[Lexer::VALUE_OFFSET], "\n\r"), + Lexer::TOKEN_HORIZONTAL_WS, + ($token[2] ?? 0) + 1, + ]; + continue; + } + + $fixed[] = $token; + } + + return new TokenIterator($fixed); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/ExtendsFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/ExtendsFactory.php new file mode 100644 index 000000000..9a5528de2 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/ExtendsFactory.php @@ -0,0 +1,52 @@ +descriptionFactory = $descriptionFactory; + $this->typeResolver = $typeResolver; + } + + public function supports(PhpDocTagNode $node, Context $context): bool + { + return $node->value instanceof ExtendsTagValueNode && $node->name === '@extends'; + } + + public function create(PhpDocTagNode $node, Context $context): Tag + { + $tagValue = $node->value; + Assert::isInstanceOf($tagValue, ExtendsTagValueNode::class); + + $description = $tagValue->getAttribute('description'); + if (is_string($description) === false) { + $description = $tagValue->description; + } + + return new Extends_( + $this->typeResolver->createType($tagValue->type, $context), + $this->descriptionFactory->create($description, $context) + ); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/Factory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/Factory.php new file mode 100644 index 000000000..190d3ff85 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/Factory.php @@ -0,0 +1,41 @@ +descriptionFactory = $descriptionFactory; + $this->typeResolver = $typeResolver; + } + + public function supports(PhpDocTagNode $node, Context $context): bool + { + return $node->value instanceof ImplementsTagValueNode && $node->name === '@implements'; + } + + public function create(PhpDocTagNode $node, Context $context): Tag + { + $tagValue = $node->value; + Assert::isInstanceOf($tagValue, ImplementsTagValueNode::class); + + $description = $tagValue->getAttribute('description'); + if (is_string($description) === false) { + $description = $tagValue->description; + } + + return new Implements_( + $this->typeResolver->createType($tagValue->type, $context), + $this->descriptionFactory->create($description, $context) + ); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/MethodFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/MethodFactory.php new file mode 100644 index 000000000..17c768f85 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/MethodFactory.php @@ -0,0 +1,83 @@ +descriptionFactory = $descriptionFactory; + $this->typeResolver = $typeResolver; + } + + public function create(PhpDocTagNode $node, Context $context): Tag + { + $tagValue = $node->value; + Assert::isInstanceOf($tagValue, MethodTagValueNode::class); + + return new Method( + $tagValue->methodName, + [], + $this->createReturnType($tagValue, $context), + $tagValue->isStatic, + $this->descriptionFactory->create($tagValue->description, $context), + false, + array_map( + function (MethodTagValueParameterNode $param) use ($context) { + return new MethodParameter( + trim($param->parameterName, '$'), + $param->type === null ? new Mixed_() : $this->typeResolver->createType( + $param->type, + $context + ), + $param->isReference, + $param->isVariadic, + $param->defaultValue === null ? + MethodParameter::NO_DEFAULT_VALUE : + (string) $param->defaultValue + ); + }, + $tagValue->parameters + ), + ); + } + + public function supports(PhpDocTagNode $node, Context $context): bool + { + return $node->value instanceof MethodTagValueNode; + } + + private function createReturnType(MethodTagValueNode $tagValue, Context $context): Type + { + if ($tagValue->returnType === null) { + return new Void_(); + } + + return $this->typeResolver->createType($tagValue->returnType, $context); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/MethodParameterFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/MethodParameterFactory.php new file mode 100644 index 000000000..da968967b --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/MethodParameterFactory.php @@ -0,0 +1,100 @@ +{$method}($defaultValue); + } + + return ''; + } + + private function formatDouble(float $defaultValue): string + { + return var_export($defaultValue, true); + } + + /** + * @param mixed $defaultValue + */ + private function formatNull($defaultValue): string + { + return 'null'; + } + + private function formatInteger(int $defaultValue): string + { + return var_export($defaultValue, true); + } + + private function formatString(string $defaultValue): string + { + return var_export($defaultValue, true); + } + + private function formatBoolean(bool $defaultValue): string + { + return var_export($defaultValue, true); + } + + /** + * @param array<(array|int|float|bool|string|object|null)> $defaultValue + */ + private function formatArray(array $defaultValue): string + { + $formatedValue = '['; + + foreach ($defaultValue as $key => $value) { + $method = 'format' . ucfirst(gettype($value)); + if (!method_exists($this, $method)) { + continue; + } + + $formatedValue .= $this->{$method}($value); + + if ($key === array_key_last($defaultValue)) { + continue; + } + + $formatedValue .= ','; + } + + return $formatedValue . ']'; + } + + private function formatObject(object $defaultValue): string + { + return 'new ' . get_class($defaultValue) . '()'; + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/PHPStanFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/PHPStanFactory.php new file mode 100644 index 000000000..cf04a06e6 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/PHPStanFactory.php @@ -0,0 +1,16 @@ +descriptionFactory = $descriptionFactory; + $this->typeResolver = $typeResolver; + } + + public function create(PhpDocTagNode $node, Context $context): Tag + { + $tagValue = $node->value; + + if ($tagValue instanceof InvalidTagValueNode) { + Deprecation::trigger( + 'phpdocumentor/reflection-docblock', + 'https://github.com/phpDocumentor/ReflectionDocBlock/issues/362', + sprintf( + 'Param tag value "%s" is invalid, falling back to legacy parsing. Please update your docblocks.', + $tagValue->value + ) + ); + + return Param::create($tagValue->value, $this->typeResolver, $this->descriptionFactory, $context); + } + + Assert::isInstanceOfAny( + $tagValue, + [ + ParamTagValueNode::class, + TypelessParamTagValueNode::class, + ] + ); + + if (($tagValue->type ?? null) instanceof OffsetAccessTypeNode) { + return InvalidTag::create( + (string) $tagValue, + 'param' + ); + } + + $description = $tagValue->getAttribute('description'); + if (is_string($description) === false) { + $description = $tagValue->description; + } + + return new Param( + trim($tagValue->parameterName, '$'), + $this->typeResolver->createType($tagValue->type ?? new IdentifierTypeNode('mixed'), $context), + $tagValue->isVariadic, + $this->descriptionFactory->create($description, $context), + $tagValue->isReference + ); + } + + public function supports(PhpDocTagNode $node, Context $context): bool + { + return $node->value instanceof ParamTagValueNode + || $node->value instanceof TypelessParamTagValueNode + || $node->name === '@param'; + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/PropertyFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/PropertyFactory.php new file mode 100644 index 000000000..b744ed08f --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/PropertyFactory.php @@ -0,0 +1,54 @@ +descriptionFactory = $descriptionFactory; + $this->typeResolver = $typeResolver; + } + + public function create(PhpDocTagNode $node, Context $context): Tag + { + $tagValue = $node->value; + Assert::isInstanceOf($tagValue, PropertyTagValueNode::class); + + $description = $tagValue->getAttribute('description'); + if (is_string($description) === false) { + $description = $tagValue->description; + } + + return new Property( + trim($tagValue->propertyName, '$'), + $this->typeResolver->createType($tagValue->type, $context), + $this->descriptionFactory->create($description, $context) + ); + } + + public function supports(PhpDocTagNode $node, Context $context): bool + { + return $node->value instanceof PropertyTagValueNode && $node->name === '@property'; + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/PropertyReadFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/PropertyReadFactory.php new file mode 100644 index 000000000..b0898aa73 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/PropertyReadFactory.php @@ -0,0 +1,54 @@ +typeResolver = $typeResolver; + $this->descriptionFactory = $descriptionFactory; + } + + public function create(PhpDocTagNode $node, Context $context): Tag + { + $tagValue = $node->value; + Assert::isInstanceOf($tagValue, PropertyTagValueNode::class); + + $description = $tagValue->getAttribute('description'); + if (is_string($description) === false) { + $description = $tagValue->description; + } + + return new PropertyRead( + trim($tagValue->propertyName, '$'), + $this->typeResolver->createType($tagValue->type, $context), + $this->descriptionFactory->create($description, $context) + ); + } + + public function supports(PhpDocTagNode $node, Context $context): bool + { + return $node->value instanceof PropertyTagValueNode && $node->name === '@property-read'; + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/PropertyWriteFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/PropertyWriteFactory.php new file mode 100644 index 000000000..749b1eda9 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/PropertyWriteFactory.php @@ -0,0 +1,54 @@ +descriptionFactory = $descriptionFactory; + $this->typeResolver = $typeResolver; + } + + public function create(PhpDocTagNode $node, Context $context): Tag + { + $tagValue = $node->value; + Assert::isInstanceOf($tagValue, PropertyTagValueNode::class); + + $description = $tagValue->getAttribute('description'); + if (is_string($description) === false) { + $description = $tagValue->description; + } + + return new PropertyWrite( + trim($tagValue->propertyName, '$'), + $this->typeResolver->createType($tagValue->type, $context), + $this->descriptionFactory->create($description, $context) + ); + } + + public function supports(PhpDocTagNode $node, Context $context): bool + { + return $node->value instanceof PropertyTagValueNode && $node->name === '@property-write'; + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/ReturnFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/ReturnFactory.php new file mode 100644 index 000000000..4a17dc245 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/ReturnFactory.php @@ -0,0 +1,52 @@ +descriptionFactory = $descriptionFactory; + $this->typeResolver = $typeResolver; + } + + public function create(PhpDocTagNode $node, Context $context): Tag + { + $tagValue = $node->value; + Assert::isInstanceOf($tagValue, ReturnTagValueNode::class); + + $description = $tagValue->getAttribute('description'); + if (is_string($description) === false) { + $description = $tagValue->description; + } + + return new Return_( + $this->typeResolver->createType($tagValue->type, $context), + $this->descriptionFactory->create($description, $context) + ); + } + + public function supports(PhpDocTagNode $node, Context $context): bool + { + return $node->value instanceof ReturnTagValueNode; + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/StaticMethod.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/StaticMethod.php new file mode 100644 index 000000000..f6f0bb5a4 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/StaticMethod.php @@ -0,0 +1,25 @@ +descriptionFactory = $descriptionFactory; + $this->typeResolver = $typeResolver; + } + + public function supports(PhpDocTagNode $node, Context $context): bool + { + return $node->value instanceof ExtendsTagValueNode && $node->name === '@template-extends'; + } + + public function create(PhpDocTagNode $node, Context $context): Tag + { + $tagValue = $node->value; + Assert::isInstanceOf($tagValue, ExtendsTagValueNode::class); + + $description = $tagValue->getAttribute('description'); + if (is_string($description) === false) { + $description = $tagValue->description; + } + + return new TemplateExtends( + $this->typeResolver->createType($tagValue->type, $context), + $this->descriptionFactory->create($description, $context) + ); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/TemplateFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/TemplateFactory.php new file mode 100644 index 000000000..9e4b3c896 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/TemplateFactory.php @@ -0,0 +1,56 @@ +descriptionFactory = $descriptionFactory; + $this->typeResolver = $typeResolver; + } + + public function create(PhpDocTagNode $node, Context $context): Tag + { + $tagValue = $node->value; + + Assert::isInstanceOf($tagValue, TemplateTagValueNode::class); + $name = $tagValue->name; + + $description = $tagValue->getAttribute('description'); + if (is_string($description) === false) { + $description = $tagValue->description; + } + + return new Template( + $name, + $this->typeResolver->createType($tagValue->bound, $context), + $this->typeResolver->createType($tagValue->default, $context), + $this->descriptionFactory->create($description, $context) + ); + } + + public function supports(PhpDocTagNode $node, Context $context): bool + { + return $node->value instanceof TemplateTagValueNode && $node->name === '@template'; + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/TemplateImplementsFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/TemplateImplementsFactory.php new file mode 100644 index 000000000..bb3d11dad --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/TemplateImplementsFactory.php @@ -0,0 +1,52 @@ +descriptionFactory = $descriptionFactory; + $this->typeResolver = $typeResolver; + } + + public function supports(PhpDocTagNode $node, Context $context): bool + { + return $node->value instanceof ImplementsTagValueNode && $node->name === '@template-implements'; + } + + public function create(PhpDocTagNode $node, Context $context): Tag + { + $tagValue = $node->value; + Assert::isInstanceOf($tagValue, ImplementsTagValueNode::class); + + $description = $tagValue->getAttribute('description'); + if (is_string($description) === false) { + $description = $tagValue->description; + } + + return new TemplateImplements( + $this->typeResolver->createType($tagValue->type, $context), + $this->descriptionFactory->create($description, $context) + ); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/VarFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/VarFactory.php new file mode 100644 index 000000000..479ceb27d --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/VarFactory.php @@ -0,0 +1,54 @@ +descriptionFactory = $descriptionFactory; + $this->typeResolver = $typeResolver; + } + + public function create(PhpDocTagNode $node, Context $context): Tag + { + $tagValue = $node->value; + Assert::isInstanceOf($tagValue, VarTagValueNode::class); + + $description = $tagValue->getAttribute('description'); + if (is_string($description) === false) { + $description = $tagValue->description; + } + + return new Var_( + trim($tagValue->variableName, '$'), + $this->typeResolver->createType($tagValue->type, $context), + $this->descriptionFactory->create($description, $context) + ); + } + + public function supports(PhpDocTagNode $node, Context $context): bool + { + return $node->value instanceof VarTagValueNode; + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Formatter.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Formatter.php new file mode 100644 index 000000000..36b9983ea --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Formatter.php @@ -0,0 +1,24 @@ +maxLen = max($this->maxLen, strlen($tag->getName())); + } + } + + /** + * Formats the given tag to return a simple plain text version. + */ + public function format(Tag $tag): string + { + return '@' . $tag->getName() . + str_repeat( + ' ', + $this->maxLen - strlen($tag->getName()) + 1 + ) . + $tag; + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Formatter/PassthroughFormatter.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Formatter/PassthroughFormatter.php new file mode 100644 index 000000000..2afdfe55d --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Formatter/PassthroughFormatter.php @@ -0,0 +1,30 @@ +getName() . ' ' . $tag); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Generic.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Generic.php new file mode 100644 index 000000000..bc1ab10c1 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Generic.php @@ -0,0 +1,89 @@ +validateTagName($name); + + $this->name = $name; + $this->description = $description; + } + + /** + * Creates a new tag that represents any unknown tag type. + * + * @return static + */ + public static function create( + string $body, + string $name = '', + ?DescriptionFactory $descriptionFactory = null, + ?TypeContext $context = null + ): self { + Assert::stringNotEmpty($name); + Assert::notNull($descriptionFactory); + + $description = $body !== '' ? $descriptionFactory->create($body, $context) : null; + + return new static($name, $description); + } + + /** + * Returns the tag as a serialized string + */ + public function __toString(): string + { + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + + return $description; + } + + /** + * Validates if the tag name matches the expected format, otherwise throws an exception. + */ + private function validateTagName(string $name): void + { + 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.' + ); + } + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Implements_.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Implements_.php new file mode 100644 index 000000000..2070828cc --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Implements_.php @@ -0,0 +1,48 @@ +name = 'implements'; + $this->type = $type; + $this->description = $description; + } + + /** + * @deprecated Create using static factory is deprecated, + * this method should not be called directly by library consumers + */ + public static function create(string $body): ?Tag + { + Deprecation::trigger( + 'phpdocumentor/reflection-docblock', + 'https://github.com/phpDocumentor/ReflectionDocBlock/issues/361', + 'Create using static factory is deprecated, this method should not be called directly + by library consumers', + ); + + return null; + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/InvalidTag.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/InvalidTag.php new file mode 100644 index 000000000..05944ab9e --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/InvalidTag.php @@ -0,0 +1,142 @@ +name = $name; + $this->body = $body; + } + + public function getException(): ?Throwable + { + return $this->throwable; + } + + public function getName(): string + { + return $this->name; + } + + public static function create(string $body, string $name = ''): self + { + return new self($name, $body); + } + + public function withError(Throwable $exception): self + { + $this->flattenExceptionBacktrace($exception); + $tag = new self($this->name, $this->body); + $tag->throwable = $exception; + + return $tag; + } + + /** + * Removes all complex types from backtrace + * + * Not all objects are serializable. So we need to remove them from the + * stored exception to be sure that we do not break existing library usage. + */ + private function flattenExceptionBacktrace(Throwable $exception): void + { + $traceProperty = (new ReflectionClass(Exception::class))->getProperty('trace'); + $traceProperty->setAccessible(true); + + do { + $trace = $exception->getTrace(); + if (isset($trace[0]['args'])) { + $trace = array_map( + function (array $call): array { + $call['args'] = array_map([$this, 'flattenArguments'], $call['args'] ?? []); + + return $call; + }, + $trace + ); + } + + $traceProperty->setValue($exception, $trace); + $exception = $exception->getPrevious(); + } while ($exception !== null); + + $traceProperty->setAccessible(false); + } + + /** + * @param mixed $value + * + * @return mixed + * + * @throws ReflectionException + */ + private function flattenArguments($value) + { + if ($value instanceof Closure) { + $closureReflection = new ReflectionFunction($value); + $value = sprintf( + '(Closure at %s:%s)', + $closureReflection->getFileName(), + $closureReflection->getStartLine() + ); + } elseif (is_object($value)) { + $value = sprintf('object(%s)', get_class($value)); + } elseif (is_resource($value)) { + $value = sprintf('resource(%s)', get_resource_type($value)); + } elseif (is_array($value)) { + $value = array_map([$this, 'flattenArguments'], $value); + } + + return $value; + } + + public function render(?Formatter $formatter = null): string + { + if ($formatter === null) { + $formatter = new Formatter\PassthroughFormatter(); + } + + return $formatter->format($this); + } + + public function __toString(): string + { + return $this->body; + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Link.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Link.php new file mode 100644 index 000000000..fcb6ec193 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Link.php @@ -0,0 +1,76 @@ +link = $link; + $this->description = $description; + } + + public static function create( + string $body, + ?DescriptionFactory $descriptionFactory = null, + ?TypeContext $context = null + ): self { + Assert::notNull($descriptionFactory); + + $parts = Utils::pregSplit('/\s+/Su', $body, 2); + $description = isset($parts[1]) ? $descriptionFactory->create($parts[1], $context) : null; + + return new static($parts[0], $description); + } + + /** + * Gets the link + */ + public function getLink(): string + { + return $this->link; + } + + /** + * Returns a string representation for this tag. + */ + public function __toString(): string + { + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + + $link = $this->link; + + return $link . ($description !== '' ? ($link !== '' ? ' ' : '') . $description : ''); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Method.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Method.php new file mode 100644 index 000000000..41e0eed19 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Method.php @@ -0,0 +1,355 @@ +> $arguments + * @param MethodParameter[] $parameters + * @phpstan-param array $arguments + */ + public function __construct( + string $methodName, + array $arguments = [], + ?Type $returnType = null, + bool $static = false, + ?Description $description = null, + bool $returnsReference = false, + ?array $parameters = null + ) { + Assert::stringNotEmpty($methodName); + + if ($returnType === null) { + $returnType = new Void_(); + } + + $arguments = $this->filterArguments($arguments); + + $this->methodName = $methodName; + $this->returnType = $returnType; + $this->isStatic = $static; + $this->description = $description; + $this->returnsReference = $returnsReference; + $this->parameters = $parameters ?? $this->fromLegacyArguments($arguments); + } + + /** + * @deprecated Create using static factory is deprecated, + * this method should not be called directly by library consumers + */ + public static function create( + string $body, + ?TypeResolver $typeResolver = null, + ?DescriptionFactory $descriptionFactory = null, + ?TypeContext $context = null + ): ?self { + trigger_error( + 'Create using static factory is deprecated, this method should not be called directly + by library consumers', + E_USER_DEPRECATED + ); + Assert::stringNotEmpty($body); + Assert::notNull($typeResolver); + Assert::notNull($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. optionally an ampersand followed or not by whitespace : as + // a reference + // 5. then optionally a word with underscores followed by () and + // whitespace : as method name as used by phpDocumentor + // 6. then a word with underscores, followed by ( and any character + // until a ) and whitespace : as method name with signature + // 7. 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+ + )? + # Returns reference + (?: + (&) + \s* + )? + # Method name + ([\w_]+) + # Arguments + (?: + \(([^\)]*)\) + )? + \s* + # Description + (.*) + $/sux', + $body, + $matches + ) + ) { + return null; + } + + [, $static, $returnType, $returnsReference, $methodName, $argumentLines, $description] = $matches; + + $static = $static === 'static'; + + if ($returnType === '') { + $returnType = 'void'; + } + + $returnsReference = $returnsReference === '&'; + + $returnType = $typeResolver->resolve($returnType, $context); + $description = $descriptionFactory->create($description, $context); + + /** @phpstan-var array $arguments */ + $arguments = []; + if ($argumentLines !== '') { + $argumentsExploded = explode(',', $argumentLines); + foreach ($argumentsExploded as $argument) { + $argument = explode(' ', self::stripRestArg(trim($argument)), 2); + if (strpos($argument[0], '$') === 0) { + $argumentName = substr($argument[0], 1); + $argumentType = new Mixed_(); + } else { + $argumentType = $typeResolver->resolve($argument[0], $context); + $argumentName = ''; + if (isset($argument[1])) { + $argument[1] = self::stripRestArg($argument[1]); + $argumentName = substr($argument[1], 1); + } + } + + $arguments[] = ['name' => $argumentName, 'type' => $argumentType]; + } + } + + return new static( + $methodName, + $arguments, + $returnType, + $static, + $description, + $returnsReference + ); + } + + /** + * Retrieves the method name. + */ + public function getMethodName(): string + { + return $this->methodName; + } + + /** + * @deprecated Method deprecated, use {@see self::getParameters()} + * + * @return array> + * @phpstan-return array + */ + public function getArguments(): array + { + trigger_error('Method deprecated, use ::getParameters()', E_USER_DEPRECATED); + + return array_map( + static function (MethodParameter $methodParameter) { + return ['name' => $methodParameter->getName(), 'type' => $methodParameter->getType()]; + }, + $this->parameters + ); + } + + /** @return MethodParameter[] */ + public function getParameters(): array + { + return $this->parameters; + } + + /** + * 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(): bool + { + return $this->isStatic; + } + + public function getReturnType(): Type + { + return $this->returnType; + } + + public function returnsReference(): bool + { + return $this->returnsReference; + } + + public function __toString(): string + { + $arguments = []; + foreach ($this->parameters as $parameter) { + $arguments[] = (string) $parameter; + } + + $argumentStr = '(' . implode(', ', $arguments) . ')'; + + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + + $static = $this->isStatic ? 'static' : ''; + + $returnType = (string) $this->returnType; + + $methodName = $this->methodName; + + $reference = $this->returnsReference ? '&' : ''; + + return $static + . ($returnType !== '' ? ($static !== '' ? ' ' : '') . $returnType : '') + . ($methodName !== '' ? ($static !== '' || $returnType !== '' ? ' ' : '') . $reference . $methodName : '') + . $argumentStr + . ($description !== '' ? ' ' . $description : ''); + } + + /** + * @param mixed[][]|string[] $arguments + * @phpstan-param array $arguments + * + * @return mixed[][] + * @phpstan-return array + */ + private function filterArguments(array $arguments = []): array + { + $result = []; + foreach ($arguments as $argument) { + if (is_string($argument)) { + $argument = ['name' => $argument]; + } + + if (!isset($argument['type'])) { + $argument['type'] = new Mixed_(); + } + + $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) + ); + } + + $result[] = $argument; + } + + return $result; + } + + private static function stripRestArg(string $argument): string + { + if (strpos($argument, '...') === 0) { + $argument = trim(substr($argument, 3)); + } + + return $argument; + } + + /** + * @param array{name: string, type: Type} $arguments + * @phpstan-param array $arguments + * + * @return MethodParameter[] + */ + private function fromLegacyArguments(array $arguments): array + { + trigger_error( + 'Create method parameters via legacy format is deprecated add parameters via the constructor', + E_USER_DEPRECATED + ); + + return array_map( + static function ($arg) { + return new MethodParameter( + $arg['name'], + $arg['type'] + ); + }, + $arguments + ); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/MethodParameter.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/MethodParameter.php new file mode 100644 index 000000000..ceb870241 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/MethodParameter.php @@ -0,0 +1,91 @@ +type = $type; + $this->isReference = $isReference; + $this->isVariadic = $isVariadic; + $this->name = $name; + $this->defaultValue = $defaultValue; + } + + public function getName(): string + { + return $this->name; + } + + public function getType(): Type + { + return $this->type; + } + + public function isReference(): bool + { + return $this->isReference; + } + + public function isVariadic(): bool + { + return $this->isVariadic; + } + + public function getDefaultValue(): ?string + { + if ($this->defaultValue === self::NO_DEFAULT_VALUE) { + return null; + } + + return (new MethodParameterFactory())->format($this->defaultValue); + } + + public function __toString(): string + { + return $this->getType() . ' ' . + ($this->isReference() ? '&' : '') . + ($this->isVariadic() ? '...' : '') . + '$' . $this->getName() . + ( + $this->defaultValue !== self::NO_DEFAULT_VALUE ? + (new MethodParameterFactory())->format($this->defaultValue) : + '' + ); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Mixin.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Mixin.php new file mode 100644 index 000000000..c15d30c11 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Mixin.php @@ -0,0 +1,51 @@ +name = 'mixin'; + $this->type = $type; + $this->description = $description; + } + + public static function create( + string $body, + ?TypeResolver $typeResolver = null, + ?DescriptionFactory $descriptionFactory = null, + ?TypeContext $context = null + ): self { + Assert::notNull($typeResolver); + Assert::notNull($descriptionFactory); + + [$type, $description] = self::extractTypeFromBody($body); + + $type = $typeResolver->resolve($type, $context); + $description = $descriptionFactory->create($description, $context); + + return new static($type, $description); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Param.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Param.php new file mode 100644 index 000000000..6d46686d5 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Param.php @@ -0,0 +1,185 @@ +name = 'param'; + $this->variableName = $variableName; + $this->type = $type; + $this->isVariadic = $isVariadic; + $this->description = $description; + $this->isReference = $isReference; + } + + /** + * @deprecated Create using static factory is deprecated, + * this method should not be called directly by library consumers + */ + public static function create( + string $body, + ?TypeResolver $typeResolver = null, + ?DescriptionFactory $descriptionFactory = null, + ?TypeContext $context = null + ): self { + Deprecation::triggerIfCalledFromOutside( + 'phpdocumentor/reflection-docblock', + 'https://github.com/phpDocumentor/ReflectionDocBlock/issues/361', + 'Create using static factory is deprecated, this method should not be called directly + by library consumers', + ); + + Assert::stringNotEmpty($body); + Assert::notNull($typeResolver); + Assert::notNull($descriptionFactory); + + [$firstPart, $body] = self::extractTypeFromBody($body); + + $type = null; + $parts = Utils::pregSplit('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE); + $variableName = ''; + $isVariadic = false; + $isReference = false; + + // if the first item that is encountered is not a variable; it is a type + if ($firstPart && !self::strStartsWithVariable($firstPart)) { + $type = $typeResolver->resolve($firstPart, $context); + } else { + // first part is not a type; we should prepend it to the parts array for further processing + array_unshift($parts, $firstPart); + } + + // if the next item starts with a $ or ...$ or &$ or &...$ it must be the variable name + if (isset($parts[0]) && self::strStartsWithVariable($parts[0])) { + $variableName = array_shift($parts); + if ($type) { + array_shift($parts); + } + + Assert::notNull($variableName); + + if (strpos($variableName, '$') === 0) { + $variableName = substr($variableName, 1); + } elseif (strpos($variableName, '&$') === 0) { + $isReference = true; + $variableName = substr($variableName, 2); + } elseif (strpos($variableName, '...$') === 0) { + $isVariadic = true; + $variableName = substr($variableName, 4); + } elseif (strpos($variableName, '&...$') === 0) { + $isVariadic = true; + $isReference = true; + $variableName = substr($variableName, 5); + } + } + + $description = $descriptionFactory->create(implode('', $parts), $context); + + return new static($variableName, $type, $isVariadic, $description, $isReference); + } + + /** + * Returns the variable's name. + */ + public function getVariableName(): ?string + { + return $this->variableName; + } + + /** + * Returns whether this tag is variadic. + */ + public function isVariadic(): bool + { + return $this->isVariadic; + } + + /** + * Returns whether this tag is passed by reference. + */ + public function isReference(): bool + { + return $this->isReference; + } + + /** + * Returns a string representation for this tag. + */ + public function __toString(): string + { + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + + $variableName = ''; + if ($this->variableName !== null && $this->variableName !== '') { + $variableName .= ($this->isReference ? '&' : '') . ($this->isVariadic ? '...' : ''); + $variableName .= '$' . $this->variableName; + } + + $type = (string) $this->type; + + return $type + . ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '') + . ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : ''); + } + + private static function strStartsWithVariable(string $str): bool + { + return strpos($str, '$') === 0 + || + strpos($str, '...$') === 0 + || + strpos($str, '&$') === 0 + || + strpos($str, '&...$') === 0; + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Property.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Property.php new file mode 100644 index 000000000..3328b0807 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Property.php @@ -0,0 +1,132 @@ +name = 'property'; + $this->variableName = $variableName; + $this->type = $type; + $this->description = $description; + } + + /** + * @deprecated Create using static factory is deprecated, + * this method should not be called directly by library consumers + */ + public static function create( + string $body, + ?TypeResolver $typeResolver = null, + ?DescriptionFactory $descriptionFactory = null, + ?TypeContext $context = null + ): self { + Deprecation::triggerIfCalledFromOutside( + 'phpdocumentor/reflection-docblock', + 'https://github.com/phpDocumentor/ReflectionDocBlock/issues/361', + 'Create using static factory is deprecated, this method should not be called directly + by library consumers', + ); + + Assert::stringNotEmpty($body); + Assert::notNull($typeResolver); + Assert::notNull($descriptionFactory); + + [$firstPart, $body] = self::extractTypeFromBody($body); + $type = null; + $parts = Utils::pregSplit('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE); + $variableName = ''; + + // if the first item that is encountered is not a variable; it is a type + if ($firstPart && $firstPart[0] !== '$') { + $type = $typeResolver->resolve($firstPart, $context); + } else { + // first part is not a type; we should prepend it to the parts array for further processing + array_unshift($parts, $firstPart); + } + + // if the next item starts with a $ it must be the variable name + if (isset($parts[0]) && strpos($parts[0], '$') === 0) { + $variableName = array_shift($parts); + if ($type) { + array_shift($parts); + } + + Assert::notNull($variableName); + + $variableName = substr($variableName, 1); + } + + $description = $descriptionFactory->create(implode('', $parts), $context); + + return new static($variableName, $type, $description); + } + + /** + * Returns the variable's name. + */ + public function getVariableName(): ?string + { + return $this->variableName; + } + + /** + * Returns a string representation for this tag. + */ + public function __toString(): string + { + if ($this->description !== null) { + $description = $this->description->render(); + } else { + $description = ''; + } + + if ($this->variableName !== null && $this->variableName !== '') { + $variableName = '$' . $this->variableName; + } else { + $variableName = ''; + } + + $type = (string) $this->type; + + return $type + . ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '') + . ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : ''); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/PropertyRead.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/PropertyRead.php new file mode 100644 index 000000000..8ac1eb02b --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/PropertyRead.php @@ -0,0 +1,132 @@ +name = 'property-read'; + $this->variableName = $variableName; + $this->type = $type; + $this->description = $description; + } + + /** + * @deprecated Create using static factory is deprecated, + * this method should not be called directly by library consumers + */ + public static function create( + string $body, + ?TypeResolver $typeResolver = null, + ?DescriptionFactory $descriptionFactory = null, + ?TypeContext $context = null + ): self { + Deprecation::triggerIfCalledFromOutside( + 'phpdocumentor/reflection-docblock', + 'https://github.com/phpDocumentor/ReflectionDocBlock/issues/361', + 'Create using static factory is deprecated, this method should not be called directly + by library consumers', + ); + + Assert::stringNotEmpty($body); + Assert::notNull($typeResolver); + Assert::notNull($descriptionFactory); + + [$firstPart, $body] = self::extractTypeFromBody($body); + $type = null; + $parts = Utils::pregSplit('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE); + $variableName = ''; + + // if the first item that is encountered is not a variable; it is a type + if ($firstPart && $firstPart[0] !== '$') { + $type = $typeResolver->resolve($firstPart, $context); + } else { + // first part is not a type; we should prepend it to the parts array for further processing + array_unshift($parts, $firstPart); + } + + // if the next item starts with a $ it must be the variable name + if (isset($parts[0]) && strpos($parts[0], '$') === 0) { + $variableName = array_shift($parts); + if ($type) { + array_shift($parts); + } + + Assert::notNull($variableName); + + $variableName = substr($variableName, 1); + } + + $description = $descriptionFactory->create(implode('', $parts), $context); + + return new static($variableName, $type, $description); + } + + /** + * Returns the variable's name. + */ + public function getVariableName(): ?string + { + return $this->variableName; + } + + /** + * Returns a string representation for this tag. + */ + public function __toString(): string + { + if ($this->description !== null) { + $description = $this->description->render(); + } else { + $description = ''; + } + + if ($this->variableName !== null && $this->variableName !== '') { + $variableName = '$' . $this->variableName; + } else { + $variableName = ''; + } + + $type = (string) $this->type; + + return $type + . ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '') + . ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : ''); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/PropertyWrite.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/PropertyWrite.php new file mode 100644 index 000000000..57e7eb103 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/PropertyWrite.php @@ -0,0 +1,132 @@ +name = 'property-write'; + $this->variableName = $variableName; + $this->type = $type; + $this->description = $description; + } + + /** + * @deprecated Create using static factory is deprecated, + * this method should not be called directly by library consumers + */ + public static function create( + string $body, + ?TypeResolver $typeResolver = null, + ?DescriptionFactory $descriptionFactory = null, + ?TypeContext $context = null + ): self { + Deprecation::triggerIfCalledFromOutside( + 'phpdocumentor/reflection-docblock', + 'https://github.com/phpDocumentor/ReflectionDocBlock/issues/361', + 'Create using static factory is deprecated, this method should not be called directly + by library consumers', + ); + + Assert::stringNotEmpty($body); + Assert::notNull($typeResolver); + Assert::notNull($descriptionFactory); + + [$firstPart, $body] = self::extractTypeFromBody($body); + $type = null; + $parts = Utils::pregSplit('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE); + $variableName = ''; + + // if the first item that is encountered is not a variable; it is a type + if ($firstPart && $firstPart[0] !== '$') { + $type = $typeResolver->resolve($firstPart, $context); + } else { + // first part is not a type; we should prepend it to the parts array for further processing + array_unshift($parts, $firstPart); + } + + // if the next item starts with a $ it must be the variable name + if (isset($parts[0]) && strpos($parts[0], '$') === 0) { + $variableName = array_shift($parts); + if ($type) { + array_shift($parts); + } + + Assert::notNull($variableName); + + $variableName = substr($variableName, 1); + } + + $description = $descriptionFactory->create(implode('', $parts), $context); + + return new static($variableName, $type, $description); + } + + /** + * Returns the variable's name. + */ + public function getVariableName(): ?string + { + return $this->variableName; + } + + /** + * Returns a string representation for this tag. + */ + public function __toString(): string + { + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + + if ($this->variableName) { + $variableName = '$' . $this->variableName; + } else { + $variableName = ''; + } + + $type = (string) $this->type; + + return $type + . ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '') + . ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : ''); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Fqsen.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Fqsen.php new file mode 100644 index 000000000..e4e7e31c6 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Fqsen.php @@ -0,0 +1,37 @@ +fqsen = $fqsen; + } + + /** + * @return string string representation of the referenced fqsen + */ + public function __toString(): string + { + return (string) $this->fqsen; + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Reference.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Reference.php new file mode 100644 index 000000000..e7dea868d --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Reference.php @@ -0,0 +1,22 @@ +uri = $uri; + } + + public function __toString(): string + { + return $this->uri; + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Return_.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Return_.php new file mode 100644 index 000000000..7e9b0c7a9 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Return_.php @@ -0,0 +1,63 @@ +name = 'return'; + $this->type = $type; + $this->description = $description; + } + + /** + * @deprecated Create using static factory is deprecated, + * this method should not be called directly by library consumers + */ + public static function create( + string $body, + ?TypeResolver $typeResolver = null, + ?DescriptionFactory $descriptionFactory = null, + ?TypeContext $context = null + ): self { + Deprecation::triggerIfCalledFromOutside( + 'phpdocumentor/reflection-docblock', + 'https://github.com/phpDocumentor/ReflectionDocBlock/issues/361', + 'Create using static factory is deprecated, this method should not be called directly + by library consumers', + ); + + Assert::notNull($typeResolver); + Assert::notNull($descriptionFactory); + + [$type, $description] = self::extractTypeFromBody($body); + + $type = $typeResolver->resolve($type, $context); + $description = $descriptionFactory->create($description, $context); + + return new static($type, $description); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/See.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/See.php new file mode 100644 index 000000000..e7330e887 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/See.php @@ -0,0 +1,104 @@ +refers = $refers; + $this->description = $description; + } + + public static function create( + string $body, + ?FqsenResolver $typeResolver = null, + ?DescriptionFactory $descriptionFactory = null, + ?TypeContext $context = null + ): self { + Assert::notNull($descriptionFactory); + + $parts = Utils::pregSplit('/\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#', $parts[0])) { + return new static(new Url($parts[0]), $description); + } + + return new static(new FqsenRef(self::resolveFqsen($parts[0], $typeResolver, $context)), $description); + } + + private static function resolveFqsen(string $parts, ?FqsenResolver $fqsenResolver, ?TypeContext $context): Fqsen + { + Assert::notNull($fqsenResolver); + $fqsenParts = explode('::', $parts); + $resolved = $fqsenResolver->resolve($fqsenParts[0], $context); + + if (!array_key_exists(1, $fqsenParts)) { + return $resolved; + } + + return new Fqsen($resolved . '::' . $fqsenParts[1]); + } + + /** + * Returns the ref of this tag. + */ + public function getReference(): Reference + { + return $this->refers; + } + + /** + * Returns a string representation of this tag. + */ + public function __toString(): string + { + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + + $refers = (string) $this->refers; + + return $refers . ($description !== '' ? ($refers !== '' ? ' ' : '') . $description : ''); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Since.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Since.php new file mode 100644 index 000000000..8ec59e91c --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Since.php @@ -0,0 +1,102 @@ +version = $version; + $this->description = $description; + } + + public static function create( + ?string $body, + ?DescriptionFactory $descriptionFactory = null, + ?TypeContext $context = null + ): ?self { + if ($body === null || $body === '') { + return new static(); + } + + $matches = []; + if (!preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $body, $matches)) { + return null; + } + + Assert::notNull($descriptionFactory); + + return new static( + $matches[1], + $descriptionFactory->create($matches[2] ?? '', $context) + ); + } + + /** + * Gets the version section of the tag. + */ + public function getVersion(): ?string + { + return $this->version; + } + + /** + * Returns a string representation for this tag. + */ + public function __toString(): string + { + if ($this->description !== null) { + $description = $this->description->render(); + } else { + $description = ''; + } + + $version = (string) $this->version; + + return $version . ($description !== '' ? ($version !== '' ? ' ' : '') . $description : ''); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Source.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Source.php new file mode 100644 index 000000000..f6b4f57fa --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Source.php @@ -0,0 +1,115 @@ +startingLine = (int) $startingLine; + $this->lineCount = $lineCount !== null ? (int) $lineCount : null; + $this->description = $description; + } + + public static function create( + string $body, + ?DescriptionFactory $descriptionFactory = null, + ?TypeContext $context = null + ): self { + 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(): int + { + 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(): ?int + { + return $this->lineCount; + } + + public function __toString(): string + { + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + + $startingLine = (string) $this->startingLine; + + $lineCount = $this->lineCount !== null ? ' ' . $this->lineCount : ''; + + return $startingLine + . $lineCount + . ($description !== '' + ? ' ' . $description + : ''); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/TagWithType.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/TagWithType.php new file mode 100644 index 000000000..89e29e5ea --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/TagWithType.php @@ -0,0 +1,87 @@ +type; + } + + /** + * @return string[] + */ + protected static function extractTypeFromBody(string $body): array + { + $type = ''; + $nestingLevel = 0; + for ($i = 0, $iMax = strlen($body); $i < $iMax; $i++) { + $character = $body[$i]; + + if ($nestingLevel === 0 && trim($character) === '') { + break; + } + + $type .= $character; + if (in_array($character, ['<', '(', '[', '{'])) { + $nestingLevel++; + continue; + } + + if (in_array($character, ['>', ')', ']', '}'])) { + $nestingLevel--; + continue; + } + } + + if ($nestingLevel < 0 || $nestingLevel > 0) { + throw new InvalidArgumentException( + sprintf('Could not find type in %s, please check for malformed notations', $body) + ); + } + + $description = trim(substr($body, strlen($type))); + + return [$type, $description]; + } + + public function __toString(): string + { + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + + $type = (string) $this->type; + + return $type . ($description !== '' ? ($type !== '' ? ' ' : '') . $description : ''); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Template.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Template.php new file mode 100644 index 000000000..cfd6b6996 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Template.php @@ -0,0 +1,92 @@ +name = 'template'; + $this->templateName = $templateName; + $this->bound = $bound; + $this->default = $default; + $this->description = $description; + } + + /** + * @deprecated Create using static factory is deprecated, + * this method should not be called directly by library consumers + */ + public static function create(string $body): ?Tag + { + Deprecation::trigger( + 'phpdocumentor/reflection-docblock', + 'https://github.com/phpDocumentor/ReflectionDocBlock/issues/361', + 'Create using static factory is deprecated, this method should not be called directly + by library consumers', + ); + + return null; + } + + public function getTemplateName(): string + { + return $this->templateName; + } + + public function getBound(): ?Type + { + return $this->bound; + } + + public function getDefault(): ?Type + { + return $this->default; + } + + public function __toString(): string + { + $bound = $this->bound !== null ? ' of ' . $this->bound : ''; + $default = $this->default !== null ? ' = ' . $this->default : ''; + + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + + return $this->templateName . $bound . $default . ($description !== '' ? ' ' . $description : ''); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/TemplateCovariant.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/TemplateCovariant.php new file mode 100644 index 000000000..d5d509872 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/TemplateCovariant.php @@ -0,0 +1,51 @@ +name = 'template-covariant'; + $this->type = $type; + $this->description = $description; + } + + public static function create( + string $body, + ?TypeResolver $typeResolver = null, + ?DescriptionFactory $descriptionFactory = null, + ?TypeContext $context = null + ): self { + Assert::notNull($typeResolver); + Assert::notNull($descriptionFactory); + + [$type, $description] = self::extractTypeFromBody($body); + + $type = $typeResolver->resolve($type, $context); + $description = $descriptionFactory->create($description, $context); + + return new static($type, $description); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/TemplateExtends.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/TemplateExtends.php new file mode 100644 index 000000000..8de4681d8 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/TemplateExtends.php @@ -0,0 +1,29 @@ +name = 'template-extends'; + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/TemplateImplements.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/TemplateImplements.php new file mode 100644 index 000000000..1fb073d99 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/TemplateImplements.php @@ -0,0 +1,29 @@ +name = 'template-implements'; + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Throws.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Throws.php new file mode 100644 index 000000000..e08184681 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Throws.php @@ -0,0 +1,51 @@ +name = 'throws'; + $this->type = $type; + $this->description = $description; + } + + public static function create( + string $body, + ?TypeResolver $typeResolver = null, + ?DescriptionFactory $descriptionFactory = null, + ?TypeContext $context = null + ): self { + Assert::notNull($typeResolver); + Assert::notNull($descriptionFactory); + + [$type, $description] = self::extractTypeFromBody($body); + + $type = $typeResolver->resolve($type, $context); + $description = $descriptionFactory->create($description, $context); + + return new static($type, $description); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Uses.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Uses.php new file mode 100644 index 000000000..d9aa3608f --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Uses.php @@ -0,0 +1,98 @@ +refers = $refers; + $this->description = $description; + } + + public static function create( + string $body, + ?FqsenResolver $resolver = null, + ?DescriptionFactory $descriptionFactory = null, + ?TypeContext $context = null + ): self { + Assert::notNull($resolver); + Assert::notNull($descriptionFactory); + + $parts = Utils::pregSplit('/\s+/Su', $body, 2); + + return new static( + self::resolveFqsen($parts[0], $resolver, $context), + $descriptionFactory->create($parts[1] ?? '', $context) + ); + } + + private static function resolveFqsen(string $parts, ?FqsenResolver $fqsenResolver, ?TypeContext $context): Fqsen + { + Assert::notNull($fqsenResolver); + $fqsenParts = explode('::', $parts); + $resolved = $fqsenResolver->resolve($fqsenParts[0], $context); + + if (!array_key_exists(1, $fqsenParts)) { + return $resolved; + } + + return new Fqsen($resolved . '::' . $fqsenParts[1]); + } + + /** + * Returns the structural element this tag refers to. + */ + public function getReference(): Fqsen + { + return $this->refers; + } + + /** + * Returns a string representation of this tag. + */ + public function __toString(): string + { + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + + $refers = (string) $this->refers; + + return $refers . ($description !== '' ? ($refers !== '' ? ' ' : '') . $description : ''); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Var_.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Var_.php new file mode 100644 index 000000000..b0bd8c21e --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Var_.php @@ -0,0 +1,132 @@ +name = 'var'; + $this->variableName = $variableName; + $this->type = $type; + $this->description = $description; + } + + /** + * @deprecated Create using static factory is deprecated, + * this method should not be called directly by library consumers + */ + public static function create( + string $body, + ?TypeResolver $typeResolver = null, + ?DescriptionFactory $descriptionFactory = null, + ?TypeContext $context = null + ): self { + Deprecation::triggerIfCalledFromOutside( + 'phpdocumentor/reflection-docblock', + 'https://github.com/phpDocumentor/ReflectionDocBlock/issues/361', + 'Create using static factory is deprecated, this method should not be called directly + by library consumers', + ); + Assert::stringNotEmpty($body); + Assert::notNull($typeResolver); + Assert::notNull($descriptionFactory); + + [$firstPart, $body] = self::extractTypeFromBody($body); + + $parts = Utils::pregSplit('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE); + $type = null; + $variableName = ''; + + // if the first item that is encountered is not a variable; it is a type + if ($firstPart && $firstPart[0] !== '$') { + $type = $typeResolver->resolve($firstPart, $context); + } else { + // first part is not a type; we should prepend it to the parts array for further processing + array_unshift($parts, $firstPart); + } + + // if the next item starts with a $ it must be the variable name + if (isset($parts[0]) && strpos($parts[0], '$') === 0) { + $variableName = array_shift($parts); + if ($type) { + array_shift($parts); + } + + Assert::notNull($variableName); + + $variableName = substr($variableName, 1); + } + + $description = $descriptionFactory->create(implode('', $parts), $context); + + return new static($variableName, $type, $description); + } + + /** + * Returns the variable's name. + */ + public function getVariableName(): ?string + { + return $this->variableName; + } + + /** + * Returns a string representation for this tag. + */ + public function __toString(): string + { + if ($this->description !== null) { + $description = $this->description->render(); + } else { + $description = ''; + } + + if ($this->variableName !== null && $this->variableName !== '') { + $variableName = '$' . $this->variableName; + } else { + $variableName = ''; + } + + $type = (string) $this->type; + + return $type + . ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '') + . ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : ''); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Version.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Version.php new file mode 100644 index 000000000..8ea96ed38 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Version.php @@ -0,0 +1,105 @@ +version = $version; + $this->description = $description; + } + + public static function create( + ?string $body, + ?DescriptionFactory $descriptionFactory = null, + ?TypeContext $context = null + ): ?self { + if ($body === null || $body === '') { + return new static(); + } + + $matches = []; + if (!preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $body, $matches)) { + return null; + } + + $description = null; + if ($descriptionFactory !== null) { + $description = $descriptionFactory->create($matches[2] ?? '', $context); + } + + return new static( + $matches[1], + $description + ); + } + + /** + * Gets the version section of the tag. + */ + public function getVersion(): ?string + { + return $this->version; + } + + /** + * Returns a string representation for this tag. + */ + public function __toString(): string + { + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + + $version = (string) $this->version; + + return $version . ($description !== '' ? ($version !== '' ? ' ' : '') . $description : ''); + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlockFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlockFactory.php new file mode 100644 index 000000000..ca33fbbb6 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlockFactory.php @@ -0,0 +1,328 @@ +descriptionFactory = $descriptionFactory; + $this->tagFactory = $tagFactory; + } + + /** + * Factory method for easy instantiation. + * + * @param array|Factory> $additionalTags + */ + public static function createInstance(array $additionalTags = []): DocBlockFactoryInterface + { + $fqsenResolver = new FqsenResolver(); + $tagFactory = new StandardTagFactory($fqsenResolver); + $descriptionFactory = new DescriptionFactory($tagFactory); + $typeResolver = new TypeResolver($fqsenResolver); + + $phpstanTagFactory = new AbstractPHPStanFactory( + new ParamFactory($typeResolver, $descriptionFactory), + new VarFactory($typeResolver, $descriptionFactory), + new ReturnFactory($typeResolver, $descriptionFactory), + new PropertyFactory($typeResolver, $descriptionFactory), + new PropertyReadFactory($typeResolver, $descriptionFactory), + new PropertyWriteFactory($typeResolver, $descriptionFactory), + new MethodFactory($typeResolver, $descriptionFactory), + new ImplementsFactory($typeResolver, $descriptionFactory), + new ExtendsFactory($typeResolver, $descriptionFactory), + new TemplateFactory($typeResolver, $descriptionFactory), + new TemplateImplementsFactory($typeResolver, $descriptionFactory), + new TemplateExtendsFactory($typeResolver, $descriptionFactory), + ); + + $tagFactory->addService($descriptionFactory); + $tagFactory->addService($typeResolver); + $tagFactory->registerTagHandler('param', $phpstanTagFactory); + $tagFactory->registerTagHandler('var', $phpstanTagFactory); + $tagFactory->registerTagHandler('return', $phpstanTagFactory); + $tagFactory->registerTagHandler('property', $phpstanTagFactory); + $tagFactory->registerTagHandler('property-read', $phpstanTagFactory); + $tagFactory->registerTagHandler('property-write', $phpstanTagFactory); + $tagFactory->registerTagHandler('method', $phpstanTagFactory); + $tagFactory->registerTagHandler('extends', $phpstanTagFactory); + $tagFactory->registerTagHandler('implements', $phpstanTagFactory); + $tagFactory->registerTagHandler('template', $phpstanTagFactory); + $tagFactory->registerTagHandler('template-extends', $phpstanTagFactory); + $tagFactory->registerTagHandler('template-implements', $phpstanTagFactory); + + $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). + */ + public function create($docblock, ?Types\Context $context = null, ?Location $location = null): DocBlock + { + 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::string($docblock); + } + + Assert::stringNotEmpty($docblock); + + if ($context === null) { + $context = new Types\Context(''); + } + + $parts = $this->splitDocBlock($this->stripDocComment($docblock)); + + [$templateMarker, $summary, $description, $tags] = $parts; + + return new DocBlock( + $summary, + $description ? $this->descriptionFactory->create($description, $context) : null, + $this->parseTagBlock($tags, $context), + $context, + $location, + $templateMarker === '#@+', + $templateMarker === '#@-' + ); + } + + /** + * @param class-string|Factory $handler + */ + public function registerTagHandler(string $tagName, $handler): void + { + $this->tagFactory->registerTagHandler($tagName, $handler); + } + + /** + * Strips the asterisks from the DocBlock comment. + * + * @param string $comment String containing the comment text. + */ + private function stripDocComment(string $comment): string + { + $comment = preg_replace('#[ \t]*(?:\/\*\*|\*\/|\*)?[ \t]?(.*)?#u', '$1', $comment); + Assert::string($comment); + $comment = trim($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); + } + + // phpcs:disable + + /** + * Splits the DocBlock into a template marker, summary, description and block of tags. + * + * @param string $comment Comment to split into the sub-parts. + * + * @return string[] containing the template marker (if any), summary, description and a string containing the tags. + * + * @author Mike van Riel for extending the regex with template marker support. + * + * @author Richard van Velzen (@_richardJ) Special thanks to Richard for the regex responsible for the split. + */ + private function splitDocBlock(string $comment): array + { + // phpcs:enable + // 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); + Assert::string($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(string $tags, Types\Context $context): array + { + $tags = $this->filterTagBlock($tags); + if ($tags === null) { + return []; + } + + $result = []; + $lines = $this->splitTagBlockIntoTagLines($tags); + foreach ($lines as $key => $tagLine) { + $result[$key] = $this->tagFactory->create(trim($tagLine), $context); + } + + return $result; + } + + /** + * @return string[] + */ + private function splitTagBlockIntoTagLines(string $tags): array + { + $result = []; + foreach (explode("\n", $tags) as $tagLine) { + if ($tagLine !== '' && strpos($tagLine, '@') === 0) { + $result[] = $tagLine; + } else { + $result[count($result) - 1] .= "\n" . $tagLine; + } + } + + return $result; + } + + private function filterTagBlock(string $tags): ?string + { + $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; + } +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlockFactoryInterface.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlockFactoryInterface.php new file mode 100644 index 000000000..cacc382e6 --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlockFactoryInterface.php @@ -0,0 +1,23 @@ +> $additionalTags + */ + public static function createInstance(array $additionalTags = []): self; + + /** + * @param string|object $docblock + */ + public function create($docblock, ?Types\Context $context = null, ?Location $location = null): DocBlock; +} diff --git a/vendor/phpdocumentor/reflection-docblock/src/Exception/PcreException.php b/vendor/phpdocumentor/reflection-docblock/src/Exception/PcreException.php new file mode 100644 index 000000000..b8b6da8cf --- /dev/null +++ b/vendor/phpdocumentor/reflection-docblock/src/Exception/PcreException.php @@ -0,0 +1,44 @@ + please note that if you want to pass partial class names that additional steps are necessary, see the + > chapter `Resolving partial classes and FQSENs` for more information. + +Where the FqsenResolver can resolve: + +- Constant expressions (i.e. `@see \MyNamespace\MY_CONSTANT`) +- Function expressions (i.e. `@see \MyNamespace\myFunction()`) +- Class expressions (i.e. `@see \MyNamespace\MyClass`) +- Interface expressions (i.e. `@see \MyNamespace\MyInterface`) +- Trait expressions (i.e. `@see \MyNamespace\MyTrait`) +- Class constant expressions (i.e. `@see \MyNamespace\MyClass::MY_CONSTANT`) +- Property expressions (i.e. `@see \MyNamespace\MyClass::$myProperty`) +- Method expressions (i.e. `@see \MyNamespace\MyClass::myMethod()`) + +## Resolving a type + +In order to resolve a type you will have to instantiate the class `\phpDocumentor\Reflection\TypeResolver` and call its `resolve` method like this: + +```php +$typeResolver = new \phpDocumentor\Reflection\TypeResolver(); +$type = $typeResolver->resolve('string|integer'); +``` + +In this example you will receive a Value Object of class `\phpDocumentor\Reflection\Types\Compound` that has two +elements, one of type `\phpDocumentor\Reflection\Types\String_` and one of type +`\phpDocumentor\Reflection\Types\Integer`. + +The real power of this resolver is in its capability to expand partial class names into fully qualified class names; but in order to do that we need an additional `\phpDocumentor\Reflection\Types\Context` class that will inform the resolver in which namespace the given expression occurs and which namespace aliases (or imports) apply. + +### Resolving nullable types + +Php 7.1 introduced nullable types e.g. `?string`. Type resolver will resolve the original type without the nullable notation `?` +just like it would do without the `?`. After that the type is wrapped in a `\phpDocumentor\Reflection\Types\Nullable` object. +The `Nullable` type has a method to fetch the actual type. + +## Resolving an FQSEN + +A Fully Qualified Structural Element Name is a reference to another element in your code bases and can be resolved using the `\phpDocumentor\Reflection\FqsenResolver` class' `resolve` method, like this: + +```php +$fqsenResolver = new \phpDocumentor\Reflection\FqsenResolver(); +$fqsen = $fqsenResolver->resolve('\phpDocumentor\Reflection\FqsenResolver::resolve()'); +``` + +In this example we resolve a Fully Qualified Structural Element Name (meaning that it includes the full namespace, class name and element name) and receive a Value Object of type `\phpDocumentor\Reflection\Fqsen`. + +The real power of this resolver is in its capability to expand partial element names into Fully Qualified Structural Element Names; but in order to do that we need an additional `\phpDocumentor\Reflection\Types\Context` class that will inform the resolver in which namespace the given expression occurs and which namespace aliases (or imports) apply. + +## Resolving partial Classes and Structural Element Names + +Perhaps the best feature of this library is that it knows how to resolve partial class names into fully qualified class names. + +For example, you have this file: + +```php +namespace My\Example; + +use phpDocumentor\Reflection\Types; + +class Classy +{ + /** + * @var Types\Context + * @see Classy::otherFunction() + */ + public function __construct($context) {} + + public function otherFunction(){} +} +``` + +Suppose that you would want to resolve (and expand) the type in the `@var` tag and the element name in the `@see` tag. + +For the resolvers to know how to expand partial names you have to provide a bit of _Context_ for them by instantiating a new class named `\phpDocumentor\Reflection\Types\Context` with the name of the namespace and the aliases that are in play. + +### Creating a Context + +You can do this by manually creating a Context like this: + +```php +$context = new \phpDocumentor\Reflection\Types\Context( + '\My\Example', + [ 'Types' => '\phpDocumentor\Reflection\Types'] +); +``` + +Or by using the `\phpDocumentor\Reflection\Types\ContextFactory` to instantiate a new context based on a Reflector object or by providing the namespace that you'd like to extract and the source code of the file in which the given type expression occurs. + +```php +$contextFactory = new \phpDocumentor\Reflection\Types\ContextFactory(); +$context = $contextFactory->createFromReflector(new ReflectionMethod('\My\Example\Classy', '__construct')); +``` + +or + +```php +$contextFactory = new \phpDocumentor\Reflection\Types\ContextFactory(); +$context = $contextFactory->createForNamespace('\My\Example', file_get_contents('My/Example/Classy.php')); +``` + +### Using the Context + +After you have obtained a Context it is just a matter of passing it along with the `resolve` method of either Resolver class as second argument and the Resolvers will take this into account when resolving partial names. + +To obtain the resolved class name for the `@var` tag in the example above you can do: + +```php +$typeResolver = new \phpDocumentor\Reflection\TypeResolver(); +$type = $typeResolver->resolve('Types\Context', $context); +``` + +When you do this you will receive an object of class `\phpDocumentor\Reflection\Types\Object_` for which you can call the `getFqsen` method to receive a Value Object that represents the complete FQSEN. So that would be `phpDocumentor\Reflection\Types\Context`. + +> Why is the FQSEN wrapped in another object `Object_`? +> +> The resolve method of the TypeResolver only returns object with the interface `Type` and the FQSEN is a common type that does not represent a Type. Also: in some cases a type can represent an "Untyped Object", meaning that it is an object (signified by the `object` keyword) but does not refer to a specific element using an FQSEN. + +Another example is on how to resolve the FQSEN of a method as can be seen with the `@see` tag in the example above. To resolve that you can do the following: + +```php +$fqsenResolver = new \phpDocumentor\Reflection\FqsenResolver(); +$type = $fqsenResolver->resolve('Classy::otherFunction()', $context); +``` + +Because Classy is a Class in the current namespace its FQSEN will have the `My\Example` namespace and by calling the `resolve` method of the FQSEN Resolver you will receive an `Fqsen` object that refers to `\My\Example\Classy::otherFunction()`. diff --git a/vendor/phpdocumentor/type-resolver/composer.json b/vendor/phpdocumentor/type-resolver/composer.json new file mode 100644 index 000000000..5c280e323 --- /dev/null +++ b/vendor/phpdocumentor/type-resolver/composer.json @@ -0,0 +1,51 @@ +{ + "name": "phpdocumentor/type-resolver", + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "require": { + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.18|^2.0", + "doctrine/deprecations": "^1.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpunit/phpunit": "^9.5", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/extension-installer": "^1.1", + "vimeo/psalm": "^4.25", + "rector/rector": "^0.13.9", + "phpbench/phpbench": "^1.2" + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "phpDocumentor\\Reflection\\": ["tests/unit", "tests/benchmark"] + } + }, + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "config": { + "platform": { + "php": "7.3.0" + }, + "allow-plugins": { + "phpstan/extension-installer": true + } + } +} diff --git a/vendor/phpdocumentor/type-resolver/docs/getting-started.rst b/vendor/phpdocumentor/type-resolver/docs/getting-started.rst new file mode 100644 index 000000000..1c8ea676a --- /dev/null +++ b/vendor/phpdocumentor/type-resolver/docs/getting-started.rst @@ -0,0 +1,32 @@ +=============== +Getting started +=============== + +On this page you will find a brief introduction on how to use the TypeResolver in your project. + +Installation +============ + +The TypeResolver is available on Packagist and can be installed using Composer: + +.. code:: bash + composer require phpdocumentor/type-resolver + + +General usage +=========== + +After you installed the TypeResolver you can use it in your project. This can be done by creating a new instance +of the :php:class:`\phpDocumentor\Reflection\TypeResolver` class and calling +:php:method:`\phpDocumentor\Reflection\TypeResolver::resolve()` with the type you want to resolve. + +.. code:: php + $typeResolver = new \phpDocumentor\Reflection\TypeResolver(); + $type = $typeResolver->resolve('string'); + echo get_class($type); // phpDocumentor\Reflection\Types\String_ + +The real power of this resolver is in its capability to expand partial class names into fully qualified class names; +but in order to do that we need an additional :php:class:`\phpDocumentor\Reflection\Types\Context` class that +will inform the resolver in which namespace the given expression occurs and which namespace aliases (or imports) apply. + +Read more about the Context class in the next section. diff --git a/vendor/phpdocumentor/type-resolver/docs/index.rst b/vendor/phpdocumentor/type-resolver/docs/index.rst new file mode 100644 index 000000000..0ff9a8b25 --- /dev/null +++ b/vendor/phpdocumentor/type-resolver/docs/index.rst @@ -0,0 +1,17 @@ +============= +Type resolver +============= + +This project part of the phpDocumentor project. It is capable of creating an object structure of the type +specifications found in the PHPDoc blocks of a project. This can be useful for static analysis of a project +or other behavior that requires knowledge of the types used in a project like automatically build forms. + +This project aims to cover all types that are available in PHPDoc and PHP itself. And is open for extension by +third party developers. + +.. toctree:: + :maxdepth: 2 + :hidden: + + index + getting-started diff --git a/vendor/phpdocumentor/type-resolver/phpdoc.dist.xml b/vendor/phpdocumentor/type-resolver/phpdoc.dist.xml new file mode 100644 index 000000000..6c9899114 --- /dev/null +++ b/vendor/phpdocumentor/type-resolver/phpdoc.dist.xml @@ -0,0 +1,46 @@ + + + Type Resolver + + build/docs + + + latest + + + src/ + + api + + + php + + + template + template-extends + template-implements + extends + implements + + phpDocumentor + + + + docs + + guides + + + +