commit ecb35a99f1802f40bfe2f3f98e82cd30299727ca Author: Rmiku <46063139+Rmiku@users.noreply.github.com> Date: Fri Dec 27 17:50:49 2024 +0800 feat: prjects init ,add patient moudle diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..8f0de65 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 + +[docker-compose.yml] +indent_size = 4 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..fcb21d3 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +* text=auto eol=lf + +*.blade.php diff=html +*.css diff=css +*.html diff=html +*.md diff=markdown +*.php diff=php + +/.github export-ignore +CHANGELOG.md export-ignore +.styleci.yml export-ignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bec2973 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +/.phpunit.cache +/node_modules +/public/build +/public/hot +/public/storage +/storage/*.key +/storage/pail +/vendor +.env +.env.backup +.env.production +.phpactor.json +.phpunit.result.cache +Homestead.json +Homestead.yaml +auth.json +npm-debug.log +yarn-error.log +/.fleet +/.idea +/.nova +/.vscode +/.zed diff --git a/README.md b/README.md new file mode 100644 index 0000000..1a4c26b --- /dev/null +++ b/README.md @@ -0,0 +1,66 @@ +

Laravel Logo

+ +

+Build Status +Total Downloads +Latest Stable Version +License +

+ +## About Laravel + +Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as: + +- [Simple, fast routing engine](https://laravel.com/docs/routing). +- [Powerful dependency injection container](https://laravel.com/docs/container). +- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage. +- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent). +- Database agnostic [schema migrations](https://laravel.com/docs/migrations). +- [Robust background job processing](https://laravel.com/docs/queues). +- [Real-time event broadcasting](https://laravel.com/docs/broadcasting). + +Laravel is accessible, powerful, and provides tools required for large, robust applications. + +## Learning Laravel + +Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework. + +You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch. + +If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library. + +## Laravel Sponsors + +We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com). + +### Premium Partners + +- **[Vehikl](https://vehikl.com/)** +- **[Tighten Co.](https://tighten.co)** +- **[WebReinvent](https://webreinvent.com/)** +- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)** +- **[64 Robots](https://64robots.com)** +- **[Curotec](https://www.curotec.com/services/technologies/laravel/)** +- **[Cyber-Duck](https://cyber-duck.co.uk)** +- **[DevSquad](https://devsquad.com/hire-laravel-developers)** +- **[Jump24](https://jump24.co.uk)** +- **[Redberry](https://redberry.international/laravel/)** +- **[Active Logic](https://activelogic.com)** +- **[byte5](https://byte5.de)** +- **[OP.GG](https://op.gg)** + +## Contributing + +Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions). + +## Code of Conduct + +In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct). + +## Security Vulnerabilities + +If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed. + +## License + +The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). diff --git a/app/Dictionary/Custom/Code.php b/app/Dictionary/Custom/Code.php new file mode 100644 index 0000000..b43e95c --- /dev/null +++ b/app/Dictionary/Custom/Code.php @@ -0,0 +1,24 @@ + '成功', + self::ERROR => '失败', + }; + } +} diff --git a/app/Dictionary/EHealthCard/CardChannel.php b/app/Dictionary/EHealthCard/CardChannel.php new file mode 100644 index 0000000..44d29a8 --- /dev/null +++ b/app/Dictionary/EHealthCard/CardChannel.php @@ -0,0 +1,34 @@ + '微信', + self::OFFICIAL_ACCOUNT => '服务号', + self::MINI_PROGRAM => '小程序', + self::OTHER => '其他', + }; + } +} diff --git a/app/Dictionary/EHealthCard/CardCostTypes.php b/app/Dictionary/EHealthCard/CardCostTypes.php new file mode 100644 index 0000000..51b6bca --- /dev/null +++ b/app/Dictionary/EHealthCard/CardCostTypes.php @@ -0,0 +1,34 @@ + '自费', + self::MEDICAL_INSURANCE => '医保', + self::PUBLIC_EXPENSE => '公费', + self::OTHER => '其他', + }; + } +} diff --git a/app/Dictionary/EHealthCard/CardType.php b/app/Dictionary/EHealthCard/CardType.php new file mode 100644 index 0000000..d40eb8c --- /dev/null +++ b/app/Dictionary/EHealthCard/CardType.php @@ -0,0 +1,80 @@ + '居民身份证', + self::RESIDENT_HOUSEHOLD_REGISTER => '居民户口簿', + self::PASSPORT => '护照', + self::OFFICER_ID_CARD => '军官证', + self::DRIVER_LICENSE => '驾驶证', + self::HK_MACAO_RESIDENT_PASS => '港澳居民来往内地通行证', + self::TAIWAN_RESIDENT_PASS => '台湾居民来往内地通行证', + self::BIRTH_CERTIFICATE => '出生医学证明', + self::MEDICAL_INSURANCE_CARD => '医保卡', + self::MEDICAL_CARD => '就诊卡', + self::ELECTRON_HEALTH_CARD => '电子健康卡', + self::NEWBORN_CERTIFICATE => '新生儿证件', + self::PERMANENT_RESIDENCE_PERMIT => '外国人永久居住证', + self::OTHER => '其他法定有效证件', + }; + } + + /** + * Patient Card Type To Electron Hath Card Type + */ + public function patientCardTypeToEHathCardType(PatientCardType $card_type): CardType + { + return match ($card_type) { + PatientCardType::ID_CARD_NO => self::RESIDENT_ID_CARD, + PatientCardType::HMT_CARD_NO => self::HK_MACAO_RESIDENT_PASS, + PatientCardType::PASSPORT => self::PASSPORT, + PatientCardType::OFFICER_ID_CARD => self::OFFICER_ID_CARD + }; + } +} diff --git a/app/Dictionary/EHealthCard/CodeType.php b/app/Dictionary/EHealthCard/CodeType.php new file mode 100644 index 0000000..9a9fd01 --- /dev/null +++ b/app/Dictionary/EHealthCard/CodeType.php @@ -0,0 +1,26 @@ + '动态码', + self::STATICS_CODE => '静态码', + }; + } +} diff --git a/app/Dictionary/EHealthCard/Scene.php b/app/Dictionary/EHealthCard/Scene.php new file mode 100644 index 0000000..a0e7c74 --- /dev/null +++ b/app/Dictionary/EHealthCard/Scene.php @@ -0,0 +1,85 @@ + '预约挂号', + self::TODAY_REGISTRATION => '当日挂号', + self::REGISTRATION_RECORD => '挂号记录', + self::SMART_GUIDANCE => '智能导诊', + self::OUTPATIENT_PAYMENT => '门诊缴费', + self::OUTPATIENT_PAYMENT_RECORD => '门诊缴费记录', + self::INPATIENT_RECHARGE => '住院充值', + self::CHECK_REPORT_QUERY => '查(取)检查报告', + self::TEST_REPORT_QUERY => '查(取)检验报告', + self::INPATIENT_RECORD => '住院记录', + self::BASIC_INFORMATION => '基本信息', + self::ONLINE_CONSULTATION => '在线问诊', + self::QUEUE_NUMBER => '排队取号', + self::APPOINTMENT_REGISTRATION_HEALTH_INSURANCE_PAYMENT => '预约挂号医保支付', + self::TODAY_REGISTRATION_HEALTH_INSURANCE_PAYMENT => '当日挂号医保支付', + self::OUTPATIENT_HEALTH_INSURANCE_PAYMENT => '门诊费(药费)医保支付', + self::ONLINE_TEXT_CONSULTATION => '图文咨询', + self::ONLINE_VIDEO_CONSULTATION => '视频咨询', + self::TELEPHONE_CONSULTATION => '电话咨询', + self::BIND_HEALTH_CARD => '绑定健康卡', + self::OTHER => '其他', + }; + } +} diff --git a/app/Dictionary/Order/NotifyStatus.php b/app/Dictionary/Order/NotifyStatus.php new file mode 100644 index 0000000..3b5f5d8 --- /dev/null +++ b/app/Dictionary/Order/NotifyStatus.php @@ -0,0 +1,27 @@ + '未接收', + self::ACCEPTED => '已接收' + + }; + } +} diff --git a/app/Dictionary/Order/PayMode.php b/app/Dictionary/Order/PayMode.php new file mode 100644 index 0000000..9b0424b --- /dev/null +++ b/app/Dictionary/Order/PayMode.php @@ -0,0 +1,26 @@ + '支付', + self::REFUND => '退费', + }; + } +} diff --git a/app/Dictionary/Order/PayType.php b/app/Dictionary/Order/PayType.php new file mode 100644 index 0000000..03d1b88 --- /dev/null +++ b/app/Dictionary/Order/PayType.php @@ -0,0 +1,50 @@ + '聚合支付', + self::UNION_PAY => '银联支付', + self::WECHAT_PAY => '微信支付', + self::ALI_PAY => '支付宝支付', + self::MEDICAL_INSURANCE_PAY => '医保支付', + }; + } + + /** + * Order id name + * @return string + */ + public function order(): string + { + return match ($this) { + self::AGGREGATION_PAY => 'JH', + self::UNION_PAY => 'YL', + self::WECHAT_PAY => 'WX', + self::ALI_PAY => 'AL', + self::MEDICAL_INSURANCE_PAY => 'YB', + }; + } +} diff --git a/app/Dictionary/Order/SourceId.php b/app/Dictionary/Order/SourceId.php new file mode 100644 index 0000000..460ec63 --- /dev/null +++ b/app/Dictionary/Order/SourceId.php @@ -0,0 +1,26 @@ + '微信公众号', + self::MINI_PROGRAM => '微信小程序', + }; + } +} diff --git a/app/Dictionary/Order/Status.php b/app/Dictionary/Order/Status.php new file mode 100644 index 0000000..835b6a4 --- /dev/null +++ b/app/Dictionary/Order/Status.php @@ -0,0 +1,35 @@ + '初始', + self::ABNORMAL => '异常', + self::SUCCESS => '成功', + self::REVERSE => '冲正', + self::FAILURE => '失败' + }; + } +} diff --git a/app/Dictionary/Order/Type.php b/app/Dictionary/Order/Type.php new file mode 100644 index 0000000..501298d --- /dev/null +++ b/app/Dictionary/Order/Type.php @@ -0,0 +1,44 @@ + '当天挂号', + self::APPOINTMENT_REGISTRATION => '预约挂号', + self::OUTPATIENT_PAYMENT => '门诊缴费', + self::INPATIENT_RECHARGE => '住院按金', + self::OUTPATIENT_PREPAID_CARD_RECHARGE => '门诊预交金充值', + self::INPATIENT_PREPAID_CARD_RECHARGE => '住院预交金充值', + self::OUTPATIENT_PREPAID_CARD_REFUND => '门诊预交金退费', + self::INPATIENT_PREPAID_CARD_REFUND => '住院预交金退费' + }; + } +} diff --git a/app/Dictionary/Patient/CardType.php b/app/Dictionary/Patient/CardType.php new file mode 100644 index 0000000..7a43687 --- /dev/null +++ b/app/Dictionary/Patient/CardType.php @@ -0,0 +1,35 @@ + '就诊卡', + self::MEDICAL_INSURANCE_CARD => '医保卡', + self::RESIDENT_ID_CARD => '居民身份证', + self::OUTPATIENT_NO => '门诊号码(门诊用)', + self::INPATIENT_NO => '住院号码(住院用)', + }; + } +} diff --git a/app/Dictionary/Patient/HealthCardStatus.php b/app/Dictionary/Patient/HealthCardStatus.php new file mode 100644 index 0000000..78d7208 --- /dev/null +++ b/app/Dictionary/Patient/HealthCardStatus.php @@ -0,0 +1,26 @@ + '已注册', + self::REGISTERED => '未注册', + }; + } +} diff --git a/app/Dictionary/Patient/IdentifyCardType.php b/app/Dictionary/Patient/IdentifyCardType.php new file mode 100644 index 0000000..eb3b68f --- /dev/null +++ b/app/Dictionary/Patient/IdentifyCardType.php @@ -0,0 +1,97 @@ + '居民身份证(户口簿)', + self::CHINESE_PEOPLES_LIBERATION_ARMY_OFFICER_CARD => '中国人民解放军军官证', + self::ARMED_POLICE_OFFICER_CARD => '中国人民武装警察警官证', + self::HK_SAR_PASSPORT_OR_MAINLAND_TRAVEL_PERMIT => '香港特区护照/港澳居民来往内地通行证', + self::MACAU_SAR_PASSPORT_OR_MAINLAND_TRAVEL_PERMIT => '澳门特区护照/港澳居民来往内地通行证', + self::TAIWAN_TRAVEL_PERMIT => '台湾居民来往大陆通行证', + self::FOREIGNER_PERMANENT_RESIDENCE_PERMIT => '外国人永久居留证', + self::FOREIGNER_PASSPORT => '外国人护照', + self::DISABLED_PERSON_CARD => '残疾人证', + self::MARTYR_OR_FAMILY_MEMBER_PROOF => '军烈属证明', + self::FOREIGNER_EMPLOYMENT_PERMIT => '外国人就业证', + self::FOREIGN_EXPERT_CERTIFICATE => '外国专家证', + self::FOREIGN_PERMANENT_REPORTER_CERTIFICATE => '外国人常驻记者证', + self::EMPLOYMENT_PERMIT_FOR_TAIWAN_HK_MACAU_RESIDENTS => '台港澳人员就业证', + self::RETURNED_EXPERT_CERTIFICATE => '回国(来华)定居专家证', + self::SOCIAL_SECURITY_CARD => '社会保障卡', + self::OTHER_IDENTITY_DOCUMENT => '其他身份证件', + }; + } + + /** + * 具体的校验规则 + * @param string $number + * @return bool + */ + public function validateNumber(string $number): bool + { + return match ($this) { + self::RESIDENT_ID_CARD => validateIDCard($number), + // 军官证 + // 规则: 军/兵/士/文/职/广/(其余中文) + "字第" + 4到8位字母或数字 + "号" + // 样本: 军字第2001988号, 士字第P011816X号 + self::CHINESE_PEOPLES_LIBERATION_ARMY_OFFICER_CARD => (bool) preg_match("/^[\x{4E00}-\x{9FA5}](字第)([0-9a-zA-Z]{4,8})(号?)$/u", $number), + // 港澳居民来往内地通行证 + // 规则: H/M + 10位数字 + // 样本: H1234567890 + self::HK_SAR_PASSPORT_OR_MAINLAND_TRAVEL_PERMIT, + self::MACAU_SAR_PASSPORT_OR_MAINLAND_TRAVEL_PERMIT => (bool) preg_match("/^([HM]\d{10}(\(\w{1}\))?)$/", $number), + // 台湾居民来往大陆通行证 + // 规则: 10位数字 + // 样本: 1234567890 + self::TAIWAN_TRAVEL_PERMIT => (bool) preg_match("/^\d{10}$/", $number), + // 2017 / 2024 外国人永居证 + self::FOREIGNER_PERMANENT_RESIDENCE_PERMIT => validateIDCard($number) || validate2017ForeignersIDCard($number), + // 护照 + // 规则: 14/15开头 + 7位数字, G + 8位数字, P + 7位数字, S/D + 7或8位数字,等 + // 样本: 12345678 或 1234567890B + // 样本: 141234567, G12345678, P1234567 + self::FOREIGNER_PASSPORT => (bool) preg_match("/^([A-z]|[\d]){5,17}$/", $number), + // 社会保障卡号码 + // 规则:公民身份证号码 / 香港特别行政区代码(HKG)+预留位(0)+港澳居民来往内地通行证号码第2-9位的终身号;澳门特别行政区代码(MAC)+预留位(0)+港澳居民来往内地通行证号码第2-9位的终身号;台湾地区代码(TWN)+预留位(0)+台湾居民来往大陆通行证号码第1-8位的终身号 + self::SOCIAL_SECURITY_CARD => validateIDCard($number) || preg_match("/^(HKG|MAC|TWN)0\d{8}$/", $number), + // 以下类型不处理,默认为false + self::DISABLED_PERSON_CARD, + self::ARMED_POLICE_OFFICER_CARD, + self::EMPLOYMENT_PERMIT_FOR_TAIWAN_HK_MACAU_RESIDENTS, + self::RETURNED_EXPERT_CERTIFICATE, + self::FOREIGN_PERMANENT_REPORTER_CERTIFICATE, + self::FOREIGN_EXPERT_CERTIFICATE, + self::MARTYR_OR_FAMILY_MEMBER_PROOF, + self::FOREIGNER_EMPLOYMENT_PERMIT, + self::OTHER_IDENTITY_DOCUMENT => false, + }; + } +} diff --git a/app/Dictionary/Patient/Nation.php b/app/Dictionary/Patient/Nation.php new file mode 100644 index 0000000..30b4ea7 --- /dev/null +++ b/app/Dictionary/Patient/Nation.php @@ -0,0 +1,135 @@ + '汉族', + self::MONGOL => '蒙古族', + self::HUI => '回族', + self::TIBETAN => '藏族', + self::UYGHUR => '维吾尔族', + self::MIAO => '苗族', + self::YI => '彝族', + self::ZHUANG => '壮族', + self::BUYI => '布依族', + self::KOREAN => '朝鲜族', + self::MANCHU => '满族', + self::DONG => '侗族', + self::YAO => '瑶族', + self::BAI => '白族', + self::TUJIA => '土家族', + self::HANI => '哈尼族', + self::KAZAKH => '哈萨克族', + self::DAI => '傣族', + self::LI => '黎族', + self::LISU => '傈僳族', + self::VA => '佤族', + self::SHE => '畲族', + self::GAOSHAN => '高山族', + self::LAKA => '拉祜族', + self::SUI => '水族', + self::DONGXIANG => '东乡族', + self::NAXI => '纳西族', + self::JINGPO => '景颇族', + self::KIRGIZ => '柯尔克孜族', + self::TU => '土族', + self::DAUR => '达斡尔族', + self::Mulao => '仫佬族', + self::QIANG => '羌族', + self::BLANG => '布朗族', + self::SALAR => '撒拉族', + self::MAONAN => '毛南族', + self::GELAO => '仡佬族', + self::XIBE => '锡伯族', + self::Achang => '阿昌族', + self::PUMI => '普米族', + self::TAJIK => '塔吉克族', + self::NU => '怒族', + self::UZBEK => '乌孜别克族', + self::RUSSIAN => '俄罗斯族', + self::EWENKI => '鄂温克族', + self::DEANG => '德昂族', + self::BAOAN => '保安族', + self::YUGUR => '裕固族', + self::JING => '京族', + self::TAT => '塔塔尔族', + self::DUOLUO => '独龙族', + self::OROGEN => '鄂伦春族', + self::HEZHEN => '赫哲族', + self::MENBA => '门巴族', + self::LHOBA => '珞巴族', + self::JINO => '基诺族', + self::OTHER => '其他', + }; + } +} diff --git a/app/Dictionary/Patient/Sex.php b/app/Dictionary/Patient/Sex.php new file mode 100644 index 0000000..4abf0b7 --- /dev/null +++ b/app/Dictionary/Patient/Sex.php @@ -0,0 +1,29 @@ + '男', + self::WOMEN => '女', + self::UNKNOWN => '不详', + }; + } +} diff --git a/app/Dictionary/PushMessage/Status.php b/app/Dictionary/PushMessage/Status.php new file mode 100644 index 0000000..2535b7f --- /dev/null +++ b/app/Dictionary/PushMessage/Status.php @@ -0,0 +1,33 @@ + '未推送', + self::SUCCESS => '推送成功', + self::FAILURE => '推送失败', + }; + } +} diff --git a/app/Dictionary/PushMessage/Type.php b/app/Dictionary/PushMessage/Type.php new file mode 100644 index 0000000..cbdfd19 --- /dev/null +++ b/app/Dictionary/PushMessage/Type.php @@ -0,0 +1,51 @@ + '公众号模板消息', + self::OFFICIAL_SINGLE_SUBSCRIBE => '公众号一次性订阅消息', + self::OFFICIAL_SUBSCRIBE => '公众号订阅消息', + self::OFFICIAL_CUSTOM => '公众号客服消息', + }; + } + + /** + * Get Open Api. + * + * @return OpenApi + */ + public function api(): OpenApi + { + return match ($this) { + self::OFFICIAL_TEMPLATE => OpenApi::SEND_TEMPLATE_MESSAGE, + self::OFFICIAL_SINGLE_SUBSCRIBE => OpenApi::SEND_SINGLE_SUBSCRIBE_MESSAGE, + self::OFFICIAL_SUBSCRIBE => OpenApi::SEND_SUBSCRIBE_MESSAGE, + self::OFFICIAL_CUSTOM => OpenApi::SEND_CUSTOM_MESSAGE, + }; + } +} diff --git a/app/Dictionary/WeChat/MiniProgram/OpenApi.php b/app/Dictionary/WeChat/MiniProgram/OpenApi.php new file mode 100644 index 0000000..df0559f --- /dev/null +++ b/app/Dictionary/WeChat/MiniProgram/OpenApi.php @@ -0,0 +1,33 @@ + '挂号成功通知', + self::REGISTRATION_FAILURE => '挂号失败通知', + self::REGISTRATION_CANCEL => '挂号取消通知', + self::OUTPATIENT_PENDING => '门诊待缴费通知', + self::OUTPATIENT_PAYMENT => '门诊缴费通知', + self::INPATIENT_RECHARGE_SUCCESS => '住院预交金支付成功通知', + self::INPATIENT_RECHARGE_FAILURE => '住院预交失败提醒', + self::REFUND => '退费通知', + self::VISIT => '就诊提醒', + self::SUSPEND_VISIT => '医生停诊通知', + self::TAKE_MEDICINE => '取药通知', + self::TODO_LIST => '待办事项通知' + }; + } +} diff --git a/app/Dictionary/WeChat/Official/TemplateId.php b/app/Dictionary/WeChat/Official/TemplateId.php new file mode 100644 index 0000000..274fdd1 --- /dev/null +++ b/app/Dictionary/WeChat/Official/TemplateId.php @@ -0,0 +1,58 @@ + '挂号成功通知', + self::REGISTRATION_FAILURE => '挂号失败通知', + self::REGISTRATION_CANCEL => '挂号取消通知', + self::OUTPATIENT_PENDING => '门诊待缴费通知', + self::OUTPATIENT_PAYMENT => '门诊缴费通知', + self::INPATIENT_RECHARGE_SUCCESS => '住院预交金支付成功通知', + self::INPATIENT_RECHARGE_FAILURE => '住院预交失败提醒', + self::REFUND => '退费通知', + self::VISIT => '就诊提醒', + self::SUSPEND_VISIT => '医生停诊通知', + self::TAKE_MEDICINE => '取药通知', + self::TODO_LIST => '待办事项通知' + }; + } +} diff --git a/app/Dictionary/WeChat/Payment/V2Api.php b/app/Dictionary/WeChat/Payment/V2Api.php new file mode 100644 index 0000000..ad03e85 --- /dev/null +++ b/app/Dictionary/WeChat/Payment/V2Api.php @@ -0,0 +1,25 @@ +status_code = $status_code; + } + + public function getStatusCode(): int + { + return $this->status_code; + } +} diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php new file mode 100644 index 0000000..40ada7f --- /dev/null +++ b/app/Http/Controllers/Auth/AuthController.php @@ -0,0 +1,52 @@ +auth_logic = new AuthLogic(); + } + + /** + * 登录接口 + * @param LoginRequest $request + * @return JsonResponse + * @throws GeneralException + */ + public function login(LoginRequest $request): JsonResponse + { + $response = $this->auth_logic->login($request->code); + + return jsonResponse(Response::HTTP_OK, 'success.', $response); + } + + /** + * 未登录跳转 + * @throws AuthenticationException + */ + public function unauthorized() + { + throw new AuthenticationException('Unauthorized.'); + } +} diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php new file mode 100644 index 0000000..8677cd5 --- /dev/null +++ b/app/Http/Controllers/Controller.php @@ -0,0 +1,8 @@ +item_logic = new ItemLogic(); + } + + /** + * 获取列表 + * @return JsonResponse + * @throws GeneralException + */ + public function lists(): JsonResponse + { + $response = $this->item_logic->getLists(); + + return jsonResponse(Response::HTTP_OK, 'success', ItemListsResource::make($response)->toArray()); + } + + /** + * 获取详情 + * @param Request $request + * @param int $type_id + * @return JsonResponse + * @throws GeneralException + */ + public function details(Request $request, int $type_id): JsonResponse + { + $response = $this->item_logic->getDetails($type_id); + + return jsonResponse(Response::HTTP_OK, 'success', ItemDetailsResource::make($response)->toArray()); + } +} diff --git a/app/Http/Controllers/Patient/PatientController.php b/app/Http/Controllers/Patient/PatientController.php new file mode 100644 index 0000000..ae63596 --- /dev/null +++ b/app/Http/Controllers/Patient/PatientController.php @@ -0,0 +1,106 @@ +patient_logic = new PatientLogic(); + } + + /** + * 获取列表 + * @return JsonResponse + */ + public function lists(): JsonResponse + { + $response = $this->patient_logic->getAllPatientLists(); + + return jsonResponse(Response::HTTP_OK, 'success', PatientListsResource::make($response)->toArray()); + } + + /** + * 获取详情 + * @param Request $request + * @param string $patient_id + * @return JsonResponse + * @throws GeneralException + */ + public function details(Request $request, string $patient_id): JsonResponse + { + $response = $this->patient_logic->getPatientDetails($patient_id); + + return jsonResponse(Response::HTTP_OK, 'success', PatientDetailsResource::make($response)->toArray()); + } + + /** + * 创建档案 + * @param CreatePatientRequest $request + * @return JsonResponse + * @throws GeneralException + */ + public function create(CreatePatientRequest $request): JsonResponse + { + $patient_id = $this->patient_logic->createPatient($request->safe()->all()); + + return jsonResponse(Response::HTTP_CREATED, 'success', ['patient_id' => $patient_id]); + } + + /** + * 绑定档案 + * @param BindPatientRequest $request + * @return JsonResponse + * @throws GeneralException + */ + public function bind(BindPatientRequest $request): JsonResponse + { + $patient_id = $this->patient_logic->bindPatient($request->safe()->all()); + + return jsonResponse(Response::HTTP_CREATED, 'success', ['patient_id' => $patient_id]); + } + + /** + * 设置默认状态 + * @param Request $request + * @param string $patient_id + * @return JsonResponse + * @throws GeneralException + */ + public function setDefault(Request $request, string $patient_id): JsonResponse + { + $this->patient_logic->setDefaultPatient($patient_id); + + return jsonResponse(Response::HTTP_OK, 'update success.'); + } + + /** + * 删除 + * @param Request $request + * @param string $patient_id + * @return JsonResponse + * @throws GeneralException + */ + public function delete(Request $request, string $patient_id): JsonResponse + { + $this->patient_logic->cancelBindPatient($patient_id); + + return jsonResponse(Response::HTTP_OK, 'delete success.'); + } +} diff --git a/app/Http/Controllers/Registration/RecordController.php b/app/Http/Controllers/Registration/RecordController.php new file mode 100644 index 0000000..6c2dbdb --- /dev/null +++ b/app/Http/Controllers/Registration/RecordController.php @@ -0,0 +1,65 @@ +record_logic = new RecordLogic(); + } + + /** + * 获取挂号记录列表 + * @param Request $request + * @param string $patient_id + * @return JsonResponse + * @throws GeneralException + */ + public function lists(Request $request, string $patient_id): JsonResponse + { + $validated = $request->validate([ + 'start_date' => 'date_format:Y-m-d', + 'end_date' => 'date_format:Y-m-d|after:start_date', + ],['messages' => [ + 'start_date.date_format' => '日期格式错误', + 'end_date.date_format' => '日期格式错误', + 'end_date.after' => '查询日期错误', + ]]); + + $response = $this->record_logic->getRecordLists($patient_id, $validated['start_date'], $validated['end_date']); + + return jsonResponse(Response::HTTP_OK, 'success', RecordListsResource::make($response)->toArray()); + } + + /** + * 退号 + * @param Request $request + * @param string $patient_id + * @param string $serial_no + * @return JsonResponse + * @throws GeneralException + */ + public function refund(Request $request, string $patient_id, string $serial_no): JsonResponse + { + $this->record_logic->refundRegisterRecord($patient_id, $serial_no); + + return jsonResponse(Response::HTTP_OK, 'refund success.'); + } +} diff --git a/app/Http/Controllers/Registration/ScheduleController.php b/app/Http/Controllers/Registration/ScheduleController.php new file mode 100644 index 0000000..a2e198d --- /dev/null +++ b/app/Http/Controllers/Registration/ScheduleController.php @@ -0,0 +1,68 @@ +schedule_logic = new ScheduleLogic(); + } + + /** + * 获取科室列表 + * @param Request $request + * @return JsonResponse + * @throws GeneralException + */ + public function dept(Request $request): JsonResponse + { + $validated = $request->validate([ + 'date' => 'required|date_format:Y-m-d', + ],['messages' => [ + 'date.required' => '请传入日期', + 'date.date_format' => '日期格式错误', + ]]); + + $response = $this->schedule_logic->getDeptLists($validated['date']); + + return jsonResponse(Response::HTTP_OK, 'success', DeptListsResource::make($response)->toArray()); + } + + /** + * 获取医生排班列表 + * @param Request $request + * @return JsonResponse + * @throws GeneralException + */ + public function doctor(Request $request): JsonResponse + { + $validated = $request->validate([ + 'date' => 'required|date_format:Y-m-d', + 'dept_id' => 'required|numeric' + ], ['messages' => [ + 'date.required' => '请传入日期', + 'date.date_format' => '日期格式错误', + 'dept_id.required' => '请传入科室ID', + 'dept_id.numeric' => '科室ID格式错误', + ]]); + + $response = $this->schedule_logic->getDoctorLists($validated['date'], $validated['dept_id']); + + return jsonResponse(Response::HTTP_OK, 'success', DoctorListsResource::make($response)->toArray()); + } +} diff --git a/app/Http/Logics/Auth/AuthLogic.php b/app/Http/Logics/Auth/AuthLogic.php new file mode 100644 index 0000000..eb7475e --- /dev/null +++ b/app/Http/Logics/Auth/AuthLogic.php @@ -0,0 +1,98 @@ +setChannel('genera'); + } + + /** + * 小程序登录 + * @throws GeneralException + */ + public function login(string $code): array + { + $session_info = $this->miniProgramLogin($code); + $token_info = $this->userLogin($session_info['open_id'], $session_info['session_key']); + + Redis::setex($token_info['access_token'], 86400 * 7, json_encode($session_info, JSON_UNESCAPED_UNICODE)); + + return $token_info; + } + + /** + * 用户登录 + * @param string $open_id + * @param string $session_key + * @return array + */ + protected function userLogin(string $open_id, string $session_key): array + { + $user = User::firstOrCreate([ + 'email' => $open_id + ], [ + 'name' => $open_id, + 'password' => bcrypt($open_id) + ]); + + // 撤销之前正在使用的所有token + $user->tokens()->delete(); + + // 获取新token + $expire_time = Carbon::now()->addDays(7); + $token_str = $user->createToken(bcrypt($session_key), ['*'], $expire_time)->plainTextToken; + [$id, $token] = explode('|', $token_str, 2); + + return [ + 'access_token' => $token ?: '', + 'token_type' => 'Bearer', + ]; + } + + /** + * 小程序登录 + * @param string $code + * @return array + * @throws GeneralException + */ + protected function miniProgramLogin(string $code): array + { + try { + $mini = getWeChatMiniProgramApp(); + $response = $mini->getUtils()->codeToSession($code); + + return [ + 'open_id' => $response['openid'], + 'session_key' => $response['session_key'], + 'union_id' => $response['unionid'] ?? '' + ]; + + } catch (HttpExceptionInterface|ExceptionInterface|Exception $e) { + //service error + $message = $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine(); + $this->error('授权登录接口报错', [$message]); + throw new GeneralException('授权登录失败,请稍后再试!', Response::HTTP_BAD_REQUEST); + } + } + +} diff --git a/app/Http/Logics/Dictionary/ItemLogic.php b/app/Http/Logics/Dictionary/ItemLogic.php new file mode 100644 index 0000000..aaea387 --- /dev/null +++ b/app/Http/Logics/Dictionary/ItemLogic.php @@ -0,0 +1,61 @@ +authInitialize(); + $this->his_soap = app('HisSoapService'); + } + + /** + * 列表 + * @throws GeneralException + */ + public function getLists() + { + $response = $this->his_soap->getDictionaryLists(); + if (!isset($response['RESULTCODE']) || $response['RESULTCODE'] !== '0') { + throw new GeneralException($response['ERRORMSG'] ?? '找不到缴费项目分类列表!', Response::HTTP_SERVICE_UNAVAILABLE); + } + + return $response; + } + + /** + * 详情 + * @param int $type_id + * @return array + * @throws GeneralException + */ + public function getDetails(int $type_id): array + { + $response = $this->his_soap->getDictionaryDetails($type_id); + if (!isset($response['RESULTCODE']) || $response['RESULTCODE'] !== '0') { + throw new GeneralException($response['ERRORMSG'] ?? '找不到缴费项目分类详情!', Response::HTTP_SERVICE_UNAVAILABLE); + } + + return $response; + } +} diff --git a/app/Http/Logics/Patient/PatientLogic.php b/app/Http/Logics/Patient/PatientLogic.php new file mode 100644 index 0000000..48b557a --- /dev/null +++ b/app/Http/Logics/Patient/PatientLogic.php @@ -0,0 +1,264 @@ +authInitialize(); + $this->patient_model = new Patient(); + $this->his_soap = app('HisSoapService'); + } + + /** + * 获取绑定患者列表 + */ + public function getAllPatientLists() + { + return $this->patient_model->getBindPatientLists($this->open_id); + } + + /** + * 卡详情 + * @param string $patient_id + * @return Patient + * @throws GeneralException + */ + public function getPatientDetails(string $patient_id): Patient + { + $info = $this->patient_model->getBindPatientInfoByPatientId($this->open_id, $patient_id); + if(empty($info)) { + throw new GeneralException('找不到该就诊卡!'); + } + + // 获取患者信息 + $response = $this->his_soap->getPatientInfo('01', $info['patient_id'], CardType::MEDICAL_CARD, $info['name']); + if (!isset($response['RESULTCODE']) || $response['RESULTCODE'] !== '0') { + throw new GeneralException($response['ERRORMSG'] ?? '找不到该就诊卡!', Response::HTTP_SERVICE_UNAVAILABLE); + } + + $info->patient_card_id = $response['MZHM'] ?? ''; + $info->card_no = substr($response['CARDNO'], 0, 3). '**********'. substr($response['CARDNO'], -3); + + return $info; + } + + /** + * 建档 + * @param array $data + * @return string + * @throws GeneralException + */ + public function createPatient(array $data): string + { + // 简单判断一下证件类型 + $card_type = getIDCardType($data['card_no']); + switch ($card_type) { + // 身份证 + case 1: + $sex = getGenderByIdCard($data['card_no']) === 1 ? Sex::MALE : Sex::WOMEN; + $birthday = getBirthdayByIdCard($data['card_no']); + break; + // 2017 / 2023 外国人永居证 + case 3: + case 4: + if ($card_type == 3) { + $sex = Sex::from((int)$data['sex']); + $birthday = getBirthdayBy2017ForeignersIDCard($data['card_no']); + } else { + $sex = getGenderByIdCard($data['card_no']) === 1 ? Sex::MALE : Sex::WOMEN; + $birthday = getBirthdayByIdCard($data['card_no']); + } + break; + default: + $sex = Sex::from((int)$data['sex']); + $birthday = &$data['birthday']; + break; + } + + //过滤名字特殊字符 + $data['name'] = replaceSpecialChar($data['name']); + $card_type = CardType::from((int)$data['card_type']); + + // 查询绑定超过X个 + $bind_count = $this->patient_model->getBindPatientCount($this->open_id); + if ($bind_count >= config('custom.max_bind_patient_count')) { + throw new GeneralException('该微信达到绑定卡上限!'); + } + + // 查询患者信息 + $response = $this->his_soap->getPatientInfo('01', $data['card_no'], $card_type, $data['name']); + $this->info('查询患者信息:', $response); + + if (isset($response['RESULTCODE']) && $response['RESULTCODE'] === '0') { + $patient_id = &$response['PATIENTID']; + + // 查询是否已绑定 + $result = $this->patient_model->getPatientInfoByPatientId($patient_id); + if ($result && $result['open_id'] == $this->open_id) { + throw new GeneralException('您已绑定该就诊卡号!'); + } + + if ($result && $result['open_id'] != $this->open_id) { + throw new GeneralException('该卡号已被其他微信用户绑定!'); + } + } else { + // 查询失败,走建档 + $response = $this->his_soap->registerCard( + $data['card_no'], + $card_type, + $data['name'], + $sex, + $birthday, + $data['card_no'], + $data['mobile_phone'], + $data['address'] + ); + $this->info('建档患者:'. $data['name']. '建档结果', $response); + + if (!isset($response['RESULTCODE']) || $response['RESULTCODE'] !== '0') { + throw new GeneralException('建档失败,失败原因:'. $response['ERRORMSG'] ?? '未知错误', Response::HTTP_SERVICE_UNAVAILABLE); + } + + $patient_id = &$response['PATIENTID']; + } + + // 写入数据库 + $result = $this->patient_model->createPatient($this->union_id, $this->open_id, $patient_id, $data['name'], $sex); + + if (!$result) { + throw new GeneralException('数据保存失败,请重试!', Response::HTTP_INTERNAL_SERVER_ERROR); + } + + return $patient_id; + } + + /** + * 绑定患者 + * @param array $data + * @return string + * @throws GeneralException + */ + public function bindPatient(array $data): string + { + //过滤名字特殊字符 + $data['name'] = replaceSpecialChar($data['name']); + $card_type = CardType::from((int)$data['card_type']); + + // 查询超过X个 + $bind_count = $this->patient_model->getBindPatientCount($this->open_id); + if ($bind_count >= config('custom.max_bind_patient_count')) { + throw new GeneralException('该微信达到绑定卡上限!'); + } + + // 查询患者信息 + $response = $this->his_soap->getPatientInfo('01', $data['card_no'], $card_type, $data['name']); + $this->info('查询患者信息:', $response); + + if (!isset($response['RESULTCODE']) || $response['RESULTCODE'] != '0') { + throw new GeneralException($response['ResultContent'] ?? '未知错误'); + } + + $patient_info = &$response; + $sex = Sex::from((int) $patient_info['SEX']); + + if ($patient_info['PATIENTID'] != $data['patient_id']) { + throw new GeneralException('该证件号已建档,但就诊卡号不匹配!'); + } + + if ($patient_info['NAME'] != $data['name']) { + throw new GeneralException('该证件号已建档,但姓名不匹配!'); + } + + if ($patient_info['CARDNO'] != $data['card_no']) { + throw new GeneralException('该就诊号已建档,但证件号码不匹配!'); + } + + // 查询是否已绑定 + $result = $this->patient_model->getPatientInfoByPatientId($data['patient_id']); + if ($result && $result['openid'] == $this->open_id) { + throw new GeneralException('您已绑定该就诊卡号!'); + } + + if ($result && $result['openid'] != $this->open_id) { + throw new GeneralException('该卡号已被其他微信用户绑定!'); + } + + // 写入数据库 + $result = $this->patient_model->createPatient($this->union_id, $this->open_id, $patient_info['PATIENTID'], $data['name'], $sex); + + if (!$result) { + throw new GeneralException('数据保存失败,请重试!', Response::HTTP_INTERNAL_SERVER_ERROR); + } + + return $patient_info['PATIENTID']; + } + + /** + * 设置默认就诊卡 + * @param string $patient_id + * @return bool + * @throws GeneralException + */ + public function setDefaultPatient(string $patient_id): bool + { + $info = $this->patient_model->getBindPatientInfoByPatientId($this->open_id, $patient_id); + if (empty($info)) { + throw new GeneralException('该就诊卡不存在!'); + } + + $result = $this->patient_model->setDefaultPatient($this->open_id, $patient_id); + if (!$result) { + throw new GeneralException('设置失败,请稍后再试!', Response::HTTP_INTERNAL_SERVER_ERROR); + } + + return true; + } + + /** + * 解绑 + * @param string $patient_id + * @return bool + * @throws GeneralException + */ + public function cancelBindPatient(string $patient_id): bool + { + $info = $this->patient_model->getBindPatientInfoByPatientId($this->open_id, $patient_id); + if (empty($info)) { + throw new GeneralException('该就诊卡不存在!'); + } + + $result = $this->patient_model->deletePatient($this->open_id, $patient_id); + if (!$result) { + throw new GeneralException('解绑失败,请稍后再试!', Response::HTTP_INTERNAL_SERVER_ERROR); + } + + return true; + } +} diff --git a/app/Http/Logics/Registration/RecordLogic.php b/app/Http/Logics/Registration/RecordLogic.php new file mode 100644 index 0000000..63d2966 --- /dev/null +++ b/app/Http/Logics/Registration/RecordLogic.php @@ -0,0 +1,167 @@ +authInitialize(); + $this->setChannel('refund'); + $this->his_soap = app('HisSoapService'); + $this->reg_record_model = new RegistrationRecord(); + $this->order_model = new Order(); + } + + /** + * 获取挂号记录列表 + * @param string $patient_id + * @param string $start_date + * @param string $end_time + * @return array + * @throws GeneralException + */ + public function getRecordLists(string $patient_id, string $start_date, string $end_time): array + { + $response = $this->his_soap->getRegisterRecordLists($patient_id, $start_date,$end_time); + + if (!isset($response['RESULTCODE']) || $response['RESULTCODE'] !== '0') { + throw new GeneralException($response['ERRORMSG'] ?? '暂无相关挂号记录!', Response::HTTP_SERVICE_UNAVAILABLE); + } + + // 缓存2小时 + Cache::set('Registration.Record.'. $this->open_id.'.'. $patient_id, json_encode($response, JSON_UNESCAPED_UNICODE), 2 * 60 *60); + return $response; + } + + /** + * 退号 + * @param string $patient_id + * @param string $reg_serial_no + * @return true + * @throws GeneralException + */ + public function refundRegisterRecord(string $patient_id, string $reg_serial_no): true + { + $cache_key = 'Registration.Record.'. $this->open_id.'.'. $patient_id; + + $record_info = Cache::get($cache_key); + if (empty($record_info)) { + throw new GeneralException($response['ERRORMSG'] ?? '查询不到需要退号的挂号记录,请重新再试!', Response::HTTP_SERVICE_UNAVAILABLE); + } + $record_info = json_decode($record_info, true); + + // 获取具体的预约详情 + $record_info = xmlArrayToListByKey($record_info, 'ITEM'); + foreach ($record_info['ITEM'] as $v) { + if ($v['VISITNO'] === $reg_serial_no) { + $info = $v; + break; + } + } + + if (empty($info)) { + throw new GeneralException('查询不到需要退号的挂号记录,请重新再试!', Response::HTTP_SERVICE_UNAVAILABLE); + } + + $this->info('患者需退号的挂号记录', $info); + + // 查询小程序上的挂号记录 + $reg_record = $this->reg_record_model->getRecordByRegID($reg_serial_no); + if (empty($reg_record) || empty($reg_record->order()->order_id)) { + throw new GeneralException('非小程序渠道挂号,请在人工窗口退号退费处理', Response::HTTP_SERVICE_UNAVAILABLE); + } + + $order_info = &$reg_record->order(); + $order_id = &$order_info->order_id; + $fee = &$order_info->self_fee; + + $this->info('患者需退号的数据库挂号记录', $reg_record->toArray()); + + // 检查是否可以退号 + $response = $this->his_soap->checkRefundRegisterStatus($reg_serial_no); + $this->info('检查是否可进行退号', $response); + + if (!isset($response['RESULTCODE']) || $response['RESULTCODE'] !== '0') { + throw new GeneralException($response['ERRORMSG'] ?? '当前挂号记录不可退号!', Response::HTTP_BAD_REQUEST); + } + + // 开始退号 + $response = $this->his_soap->refundRegister($reg_serial_no, $order_id, date('Y-m-d'), date('H:i:s'), (string) ($fee / 100)); + $this->info('退号结果', $response); + + if (!isset($response['RESULTCODE']) || $response['RESULTCODE'] !== '0') { + throw new GeneralException($response['ERRORMSG'] ?? '退号失败,请重新再试!', Response::HTTP_BAD_REQUEST); + } + + // 创建退款单 + $refund_order_id = $this->order_model->getRefundOrderId($order_id); + $refund_order_info = $this->order_model->createRefundOReverseOrder( + $order_info->id, + $refund_order_id, + PayType::from($order_info->pay_type), + $fee, + $this->open_id, + $patient_id, + $order_info->patient_name, + Type::from($order_info->order_type), + SourceId::from($order_info->source_id) + ); + $this->info('创建退款订单', ['id' => $refund_order_info->id]); + + if (empty($refund_order_info)) { + throw new GeneralException($response['ERRORMSG'] ?? '退号成功,退费失败,请重新再试!', Response::HTTP_BAD_REQUEST); + } + + // 退款 + try { + $refund_order_obj = new RefundOrder($order_id, $refund_order_id, $fee, '患者自行退号退费'); + $response = Unify::common(env('unify'))->order->refund($refund_order_obj); + $this->info('退号退费结果', $response); + + } catch (ReflectionException $e) { + $this->order_model->reverseOrderOpera($refund_order_id, $fee, false); + + throw new GeneralException($e->getMessage() ?? '退号成功,退费失败,请重新再试!', Response::HTTP_SERVICE_UNAVAILABLE); + } + + if (empty($response) || $response['status'] !== 200 || $response['success'] !== true) { + $this->order_model->reverseOrderOpera($refund_order_id, $fee, false); + + throw new GeneralException($response['msg'] ?? '退号成功,退费失败,请重新再试!', Response::HTTP_BAD_REQUEST); + } + + $this->order_model->reverseOrderOpera($refund_order_id, $fee, true); + return true; + } +} diff --git a/app/Http/Logics/Registration/ScheduleLogic.php b/app/Http/Logics/Registration/ScheduleLogic.php new file mode 100644 index 0000000..f3e5025 --- /dev/null +++ b/app/Http/Logics/Registration/ScheduleLogic.php @@ -0,0 +1,60 @@ +his_soap = app('HisSoapService'); + } + + /** + * 获取科室列表 + * @param string $date + * @return array + * @throws GeneralException + */ + public function getDeptLists(string $date): array + { + $response = $this->his_soap->getDepLists('', '','01', $date); + + if (!isset($response['RESULTCODE']) || $response['RESULTCODE'] !== '0') { + throw new GeneralException($response['ERRORMSG'] ?? '暂无科室排班!', Response::HTTP_SERVICE_UNAVAILABLE); + } + + return $response; + } + + /** + * 获取医生列表 + * @param string $date 日期 + * @param string $dept_id 科室ID + * @return array + * @throws GeneralException + */ + public function getDoctorLists(string $date, string $dept_id): array + { + $type = $date === date('Y-m-d') ? '3' : '1'; + + $response = $this->his_soap->getDoctorLists($dept_id, $type, '', $date); + if (!isset($response['RESULTCODE']) || $response['RESULTCODE'] !== '0') { + throw new GeneralException($response['ERRORMSG'] ?? '该科室暂无医生排班!', Response::HTTP_SERVICE_UNAVAILABLE); + } + + return $response; + } +} diff --git a/app/Http/Middleware/PerformanceDebug.php b/app/Http/Middleware/PerformanceDebug.php new file mode 100644 index 0000000..90afab1 --- /dev/null +++ b/app/Http/Middleware/PerformanceDebug.php @@ -0,0 +1,24 @@ +isLocal()) { + + // 计算包含了多少文件 + $included_files_count = count(get_included_files()); + + dd($included_files_count); + } + + return $response; + } +} \ No newline at end of file diff --git a/app/Http/Middleware/RecordApiLog.php b/app/Http/Middleware/RecordApiLog.php new file mode 100644 index 0000000..ce8a6e5 --- /dev/null +++ b/app/Http/Middleware/RecordApiLog.php @@ -0,0 +1,72 @@ +attributes->set('start_time', microtime(true)); + + return $next($request); + } + + /** + * @param Request $request + * @param Response|JsonResponse $response + */ + public function terminate(Request $request, Response|JsonResponse $response): void + { + //结束时间 + $end = microtime(true); + $diff = $end - $request->attributes->get('start_time'); + + $ip = json_encode($request->ips(),256); + + if ($response instanceof JsonResponse) { + $responseStr = $this->getJsonResponseStr($response); + } else { + $responseStr = $this->getResponseStr($response); + } + + recordLog('RecordApiUse', implode("\n", [ + '请求地址: '. $ip. '|'. $request->method(). '|'. $request->url(), + '请求入参: '. json_encode(request()->all(), JSON_UNESCAPED_UNICODE), + '请求出参: '. $responseStr, + '耗时: '. sprintf("%.6f", $diff). 's' + ])); + } + + /** + * 获取响应字符串 + * @param Response $response + * @return string + */ + protected function getResponseStr(Response $response): string + { + return PHP_EOL. sprintf('HTTP/%s %s %s', $response->getProtocolVersion(), $response->getStatusCode(), Response::$statusTexts[$response->getStatusCode()]). PHP_EOL. + $response->headers. PHP_EOL. + json_encode($response->getContent(), JSON_UNESCAPED_UNICODE); + } + + /** + * 获取json相应字符串 + * @param JsonResponse $response + * @return string + */ + protected function getJsonResponseStr(JsonResponse $response): string + { + return PHP_EOL. sprintf('HTTP/%s %s %s', $response->getProtocolVersion(), $response->getStatusCode(), Response::$statusTexts[$response->getStatusCode()]). PHP_EOL. + $response->headers. PHP_EOL. json_encode($response->getData(), JSON_UNESCAPED_UNICODE); + } +} diff --git a/app/Http/Requests/Auth/LoginRequest.php b/app/Http/Requests/Auth/LoginRequest.php new file mode 100644 index 0000000..c84c271 --- /dev/null +++ b/app/Http/Requests/Auth/LoginRequest.php @@ -0,0 +1,44 @@ + + */ + public function rules(): array + { + return [ +// 'username' => 'required', +// 'password' => 'required', + 'code' => 'required' + ]; + } + + /** + * @return array + */ + public function messages(): array + { + return [ + 'username.required' => '入参错误', + 'password.required' => '入参错误', + 'code.required' => '入参错误' + ]; + } +} diff --git a/app/Http/Requests/Patient/BindPatientRequest.php b/app/Http/Requests/Patient/BindPatientRequest.php new file mode 100644 index 0000000..ae0df08 --- /dev/null +++ b/app/Http/Requests/Patient/BindPatientRequest.php @@ -0,0 +1,92 @@ + + */ + /** + * 规则 + * @return array + */ + public function rules(): array + { + return [ + 'name' => 'required', + 'card_type' => 'required', + 'card_no' => ['required', function ($attribute, $value, $fail) { + $card_type = getIDCardType($value); + + switch ($card_type) { + // 未知证件,身份证,港澳台社保卡号 + case 0: + case 1: + if (!IdentifyCardType::RESIDENT_ID_CARD->validateNumber($value)) { + $fail('证件号码格式错误'); + } + break; + case 2: + if (!IdentifyCardType::SOCIAL_SECURITY_CARD->validateNumber($value)) { + $fail('证件号码格式错误'); + } + break; + // 2017/2023 版永居证 + case 3: + case 4: + if (!IdentifyCardType::FOREIGNER_PERMANENT_RESIDENCE_PERMIT->validateNumber($value)) { + $fail('证件号码格式错误'); + } + break; + } + }], + 'patient_id' => 'required|max:20' + ]; + } + + /** + * 错误提示语句 + * @return array + */ + public function messages(): array + { + return [ + 'name.required' => '必须填写名称', + 'name.max' => '名称限制最大长度为50', + 'card_type.required' => '必须选择证件类型', + 'card_type.enum' => '证件类型选择错误', + 'card_no.required' => '必须填写证件号码', + 'patient_id.required' => '必须填写就诊卡号', + 'patient_id.max' => '就诊卡号长度最大为20' + ]; + } + + /** + * 字段名称 + * @return array + */ + public function attributes(): array + { + return [ + 'name' => '名称', + 'identity_type' => '证件类型', + 'identity_no' => '证件号码', + 'patient_id' => '就诊卡号', + ]; + } +} diff --git a/app/Http/Requests/Patient/CreatePatientRequest.php b/app/Http/Requests/Patient/CreatePatientRequest.php new file mode 100644 index 0000000..c0df7d8 --- /dev/null +++ b/app/Http/Requests/Patient/CreatePatientRequest.php @@ -0,0 +1,99 @@ + + */ + /** + * 规则 + * @return array + */ + public function rules(): array + { + return [ + 'name' => 'required', + 'card_type' => 'required', + 'card_no' => ['required', function ($attribute, $value, $fail) { + $card_type = getIDCardType($value); + + switch ($card_type) { + // 未知证件,身份证,港澳台社保卡号 + case 0: + case 1: + if (!IdentifyCardType::RESIDENT_ID_CARD->validateNumber($value)) { + $fail('证件号码格式错误'); + } + break; + case 2: + if (!IdentifyCardType::SOCIAL_SECURITY_CARD->validateNumber($value)) { + $fail('证件号码格式错误'); + } + break; + // 2017/2023 版永居证 + case 3: + case 4: + if (!IdentifyCardType::FOREIGNER_PERMANENT_RESIDENCE_PERMIT->validateNumber($value)) { + $fail('证件号码格式错误'); + } + break; + } + }], + 'phone' => ['required', function ($attribute, $value, $fail) { + if (!checkMobilePhone($value)) { + $fail('联系号码格式错误'); + } + }], + 'birthday' => 'required|date_format:Y-m-d', + 'sex' => 'required|in:1,2', +// 'nation' => 'max:50', + 'address' => 'required|max:100' + ]; + } + + /** + * 错误提示语句 + * @return array + */ + public function messages(): array + { + return [ + 'sex.required' => '必须选择性别' + ]; + } + + /** + * 字段名称 + * @return array + */ + public function attributes(): array + { + return [ + 'name' => '名称', + 'card_type' => '证件类型', + 'card_no' => '证件号码', + 'phone' => '联系号码', + 'sex' => '性别', + 'birthday' => '生日', + 'nationality' => '国籍', + 'nation' => '民族', + 'address' => '住址', + ]; + } +} diff --git a/app/Http/Resources/Dictionary/ItemDetailsResource.php b/app/Http/Resources/Dictionary/ItemDetailsResource.php new file mode 100644 index 0000000..35b4107 --- /dev/null +++ b/app/Http/Resources/Dictionary/ItemDetailsResource.php @@ -0,0 +1,31 @@ + + */ + public function toArray(Request $request = null): array + { + $lists = []; + foreach ($this->resource['ITEM'] as $v) { + $lists[] = [ + 'type_name' => $v['TYPENAME'], + 'item_name' => $v['COSTNAME'], + 'unit' => $v['UNIT'], + 'spec' => $v['COSTSPEC'], + 'price' => $v['PRICE'], + 'cd_name' => $v['CDNAME'] ?? '', + ]; + } + + return $lists; + } +} diff --git a/app/Http/Resources/Dictionary/ItemListsResource.php b/app/Http/Resources/Dictionary/ItemListsResource.php new file mode 100644 index 0000000..89ba78d --- /dev/null +++ b/app/Http/Resources/Dictionary/ItemListsResource.php @@ -0,0 +1,27 @@ + + */ + public function toArray(Request $request = null): array + { + $lists = []; + foreach ($this->resource['ITEM'] as $v) { + $lists[] = [ + 'type_id' => (int) $v['TYPEID'], + 'type_name' => $v['TYPENAME'], + ]; + } + + return $lists; + } +} diff --git a/app/Http/Resources/Patient/PatientDetailsResource.php b/app/Http/Resources/Patient/PatientDetailsResource.php new file mode 100644 index 0000000..15d89c5 --- /dev/null +++ b/app/Http/Resources/Patient/PatientDetailsResource.php @@ -0,0 +1,26 @@ + + */ + public function toArray(Request $request = null): array + { + return [ + 'patient_id' => $this->resource['patient_id'], + 'patient_card_id' => $this->resource['patient_card_id'], + 'patient_name' => $this->resource['name'], + 'card_no' => $this->resource['card_no'], + 'sex' => $this->resource['sex'], + 'is_default' => $this->resource['def_status'] + ]; + } +} diff --git a/app/Http/Resources/Patient/PatientListsResource.php b/app/Http/Resources/Patient/PatientListsResource.php new file mode 100644 index 0000000..0829fa6 --- /dev/null +++ b/app/Http/Resources/Patient/PatientListsResource.php @@ -0,0 +1,28 @@ + + */ + public function toArray(Request $request = null): array + { + $lists = []; + foreach ($this->resource as $v) { + $lists[] = [ + 'patient_id' => $v['patient_id'], + 'patient_name' => $v['name'], + 'is_default' => $v['def_status'] + ]; + } + + return $lists; + } +} diff --git a/app/Http/Resources/Registration/Record/RecordListsResource.php b/app/Http/Resources/Registration/Record/RecordListsResource.php new file mode 100644 index 0000000..9d66f29 --- /dev/null +++ b/app/Http/Resources/Registration/Record/RecordListsResource.php @@ -0,0 +1,44 @@ + + */ + public function toArray(Request $request = null): array + { + $this->resource = xmlArrayToListByKey($this->resource, 'ITEM'); + + $lists = []; + foreach ($this->resource['ITEM'] as $v) { + $lists[] = [ + 'reg_serial_no' => $v['VISITNO'], + 'reg_type' => $v['FTYPE'], + 'reg_date' => $v['GHDATE'], + 'patient_id' => $v['PATIENTID'], + 'patient_name' => $v['PATIENTNAME'], + 'dept_id' => $v['DEPID'], + 'dept_name' => $v['DEPNAME'], + 'rank_id' => $v['RANKID'], + 'rank_name' => $v['RANKNAME'] ?? '', + 'start_time' => $v['STARTTIME'] ?? '', + 'end_time' => $v['ENDTIME'] ?? '', + 'dept_location' => $v['DEPLOCATION'], + 'reg_fee' => $v['PAYFEE'], + 'trea_id' => $v['TREAID'], + 'tran_snum' => $v['TRANSNUM'], + 'wait_no' => $v['WAITNUM'], + 'reg_status' => $v['STATUS'], + ]; + } + + return $lists; + } +} diff --git a/app/Http/Resources/Registration/Schedule/DeptListsResource.php b/app/Http/Resources/Registration/Schedule/DeptListsResource.php new file mode 100644 index 0000000..c6912c1 --- /dev/null +++ b/app/Http/Resources/Registration/Schedule/DeptListsResource.php @@ -0,0 +1,30 @@ + + */ + public function toArray(Request $request = null): array + { + $this->resource = xmlArrayToListByKey($this->resource, 'ITEM'); + + $lists = []; + foreach ($this->resource['ITEM'] as $v) { + $lists[] = [ + 'dept_id' => $v['DEPID'], + 'dept_name' => $v['DEPNAME'], + 'dept_intro' => $v['INTRODUCE'] + ]; + } + + return $lists; + } +} diff --git a/app/Http/Resources/Registration/Schedule/DoctorListsResource.php b/app/Http/Resources/Registration/Schedule/DoctorListsResource.php new file mode 100644 index 0000000..50ae98c --- /dev/null +++ b/app/Http/Resources/Registration/Schedule/DoctorListsResource.php @@ -0,0 +1,48 @@ + + */ + public function toArray(Request $request = null): array + { + $this->resource = xmlArrayToListByKey($this->resource, 'ITEM'); + + $lists = []; + foreach ($this->resource['ITEM'] as $k=>$v) { + $lists[$k] = [ + 'doctor_id' => $v['DOCTID'], + 'doctor_name' => $v['DOCTNAME'], + 'doctor_title' => $v['TYPENAME'], + 'doctor_intro' => $v['DEPLOCATION'], + 'is_doctor' => (int) $v['ISKSDOC'], + ]; + + $v = xmlArrayToListByKey($v, 'SHIFT'); + foreach ($v['SHIFT'] as $k2=>$v2) { + $lists[$k]['schedule_lists'][$k2] = [ + 'reg_id' => $v2['REGID'], + 'date' => $v2['FDATE'], + 'rank_id' => $v2['RANKID'], + 'rank_name' => $v2['RANKNAME'], + 'start_time' => $v2['STARTTIME'], + 'end_time' => $v2['ENDTIME'], + 'fee' => $v2['FEE'], + 'fee_code' => $v2['FEECODE'], + 'reg_count' => $v2['REGCOUNT'], + 'no_visit_count' => $v2['JZCOUNT'], + ]; + } + } + + return $lists; + } +} diff --git a/app/Models/Department.php b/app/Models/Department.php new file mode 100644 index 0000000..ffe2733 --- /dev/null +++ b/app/Models/Department.php @@ -0,0 +1,41 @@ + + */ + protected $fillable = [ + 'dept_id', + 'type_id', + 'dept_name', + 'leader_name', + 'image', + 'location', + 'telephone', + 'sort', + 'intro', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'id' => 'integer', + 'type_id' => 'integer', + 'sort' => 'integer', + ]; +} diff --git a/app/Models/DepartmentType.php b/app/Models/DepartmentType.php new file mode 100644 index 0000000..29ae9a1 --- /dev/null +++ b/app/Models/DepartmentType.php @@ -0,0 +1,36 @@ + + */ + protected $fillable = [ + 'type_id', + 'type_name', + 'sort', + 'icon', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'id' => 'integer', + 'type_id' => 'integer', + 'sort' => 'integer', + ]; +} diff --git a/app/Models/Doctor.php b/app/Models/Doctor.php new file mode 100644 index 0000000..c07c48d --- /dev/null +++ b/app/Models/Doctor.php @@ -0,0 +1,40 @@ + + */ + protected $fillable = [ + 'doctor_id', + 'doctor_name', + 'dept_id', + 'dept_name', + 'title', + 'image', + 'sort', + 'adept', + 'intro', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'id' => 'integer', + 'sort' => 'integer', + ]; +} diff --git a/app/Models/HealthCardReport.php b/app/Models/HealthCardReport.php new file mode 100644 index 0000000..e120478 --- /dev/null +++ b/app/Models/HealthCardReport.php @@ -0,0 +1,57 @@ + + */ + protected $fillable = [ + 'relate_order_id', + 'relate_patient_id', + 'scene', + 'report_data', + 'number', + 'status', + 'report_at', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'id' => 'integer', + 'number' => 'integer', + 'status' => 'integer', + 'report_at' => 'datetime', + ]; + + /** + * Relationships Order. + */ + public function order(): BelongsTo + { + return $this->belongsTo(Order::class, 'relate_order_id'); + } + + /** + * Relationships Patient. + */ + public function patient(): BelongsTo + { + return $this->belongsTo(Patient::class, 'relate_patient_id'); + } +} diff --git a/app/Models/Inpatient.php b/app/Models/Inpatient.php new file mode 100644 index 0000000..64d51b8 --- /dev/null +++ b/app/Models/Inpatient.php @@ -0,0 +1,44 @@ + + */ + protected $fillable = [ + 'open_id', + 'inpatient_id', + 'inpatient_name', + 'in_time', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'id' => 'integer', + 'in_time' => 'datetime', + ]; + + /** + * Relationships inpatientRechargeRecord. + */ + public function inpatientRechargeRecord(): HasMany + { + return $this->hasMany(InpatientRechargeRecord::class, 'relate_id'); + } +} diff --git a/app/Models/InpatientRechargeRecord.php b/app/Models/InpatientRechargeRecord.php new file mode 100644 index 0000000..4a9a139 --- /dev/null +++ b/app/Models/InpatientRechargeRecord.php @@ -0,0 +1,58 @@ + + */ + protected $fillable = [ + 'relate_id', + 'relate_order_id', + 'dept_id', + 'dept_name', + 'doctor_id', + 'doctor_name', + 'recharge_amount', + 'extra_info', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'id' => 'integer', + 'relate_id' => 'integer', + 'relate_order_id' => 'integer', + 'recharge_amount' => 'integer', + ]; + + /** + * Relationships Order. + */ + public function order(): BelongsTo + { + return $this->belongsTo(Order::class, 'relate_order_id'); + } + + /** + * Relationships Inpatient. + */ + public function inpatient(): BelongsTo + { + return $this->belongsTo(Inpatient::class, 'relate_id'); + } +} diff --git a/app/Models/Order.php b/app/Models/Order.php new file mode 100644 index 0000000..9b99ea9 --- /dev/null +++ b/app/Models/Order.php @@ -0,0 +1,416 @@ + + */ + protected $fillable = [ + 'relate_id', + 'order_id', + 'his_order_id', + 'transaction_id', + 'status', + 'notify_status', + 'type', + 'pay_type', + 'pay_mode', + 'fee', + 'self_fee', + 'reduce_fee', + 'refund_fee', + 'open_id', + 'patient_id', + 'patient_name', + 'source_id', + 'payment_at', + 'refunded_at', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'id' => 'integer', + 'relate_id' => 'integer', + 'status' => 'integer', + 'type' => 'integer', + 'pay_type' => 'integer', + 'pay_mode' => 'integer', + 'fee' => 'integer', + 'self_fee' => 'integer', + 'reduce_fee' => 'integer', + 'refund_fee' => 'integer', + 'source_id' => 'integer', + 'payment_at' => 'datetime', + 'refunded_at' => 'datetime', + ]; + + /** + * Relationships Patient. + */ + public function patient(): belongsTo + { + return $this->belongsTo(Patient::class, 'patient_id', 'patient_id'); + } + + /** + * Relationships Self Order. + */ + public function order(): HasMany + { + return $this->hasMany(__CLASS__, 'relate_id'); + } + + /** + * Relationships RegistrationRecord Model. + * @return HasOne + */ + public function registrationRecord(): HasOne + { + return $this->hasOne(RegistrationRecord::class, 'relate_order_id'); + } + + /** + * Relationships OutpatientPaymentRecord Model. + * @return HasOne + */ + public function outpatientPaymentRecord(): HasOne + { + return $this->hasOne(OutpatientPaymentRecord::class, 'relate_order_id'); + } + + /** + * 获取订单ID + * @param PayType $pay_type 支付类型 + * @param string $user W 微信 D 大机器 X 小机器 H his + * @return string + */ + public function getOrderId(PayType $pay_type, string $user = 'D'): string + { + $order_id = $pay_type->order(). $user. date('YmdHis'). mt_rand(100, 999); + if ($this->where('order_id', $order_id)->first()) { + return $this->getOrderId($pay_type, $user); + } + + return $order_id; + } + + /** + * 获取退款订单ID + * @param string $order_id 原订单ID + * @param string $source_flag 退款来源标志 _ 程序自动冲正 R 人工退费 H His退款 + * @return string + */ + public function getRefundOrderId(string $order_id, string $source_flag = '_'): string + { + $refund_order_id = $order_id. $source_flag. mt_rand(100, 999); + if ($this->where('order_id', $refund_order_id)->first()) { + return $this->getRefundOrderId($order_id); + } + + return $refund_order_id; + } + + /** + * 获取订单详情by id + * @param string $id + * @return mixed + */ + public function getOrderInfoById(string $id): mixed + { + return $this->where('id', $id)->with('patient')->first(); + } + + /** + * 获取订单详情by order_id + * @param string $order_id + * @return mixed + */ + public function getOrderInfoByOrderId(string $order_id): mixed + { + return $this->where('order_id', $order_id)->with('patient')->first(); + } + + /** + * 创建订单 + * @param string $order_id + * @param PayType $pay_type + * @param float $fee + * @param string $open_id + * @param string $patient_id + * @param string $patient_name + * @param Type $order_type + * @param SourceId $source_id + * @param array $reg_info + * @param array $outpatient_info + * @return mixed + */ + public function createOrder(string $order_id, PayType $pay_type, float $fee, string $open_id, string $patient_id, string $patient_name, Type $order_type, SourceId $source_id, array $reg_info = [], array $outpatient_info = []): mixed + { + + $order_info = [ + 'relate_id' => 0, + 'order_id' => $order_id, + 'his_order_id' => '', + 'transaction_id' => '', + 'status' => Status::NORMAL->value, + 'notify_status' => NotifyStatus::NO_ACCEPTED->value, + 'type' => $order_type->value, + 'pay_type' => $pay_type, + 'pay_mode' => PayMode::PAYMENT->value, + 'fee' => $fee, //分 + 'self_fee' => $fee, //分 + 'refund_fee' => 0, + 'open_id' => $open_id, + 'patient_id' => $patient_id, + 'patient_name' => $patient_name, + 'source_id' => $source_id, + ]; + + // 操作订单记录进行创建操作 + if (in_array($order_type->value, [ + Type::TODAY_REGISTRATION->value, + Type::APPOINTMENT_REGISTRATION->value, + Type::OUTPATIENT_PAYMENT->value + ])) { + $result = $this->create($order_info); + + if (!empty($result)) { + switch ($order_type->value) { + case Type::TODAY_REGISTRATION->value: + case Type::APPOINTMENT_REGISTRATION->value: + $result->registrationRecord()->create($reg_info); + break; + case Type::OUTPATIENT_PAYMENT->value: + $result->outpatientPaymentRecord()->create($outpatient_info); + break; + default: + break; + } + } + + return $result; + } + + return $this->create($order_info); + } + + /** + * 创建退费/冲正订单 + * @param int $relate_order_id + * @param string $order_id + * @param PayType $pay_type + * @param float $fee + * @param string $open_id + * @param string $patient_id + * @param string $patient_name + * @param Type $order_type + * @param SourceId $source_id + * @return mixed + */ + public function createRefundOReverseOrder(int $relate_order_id, string $order_id, PayType $pay_type, float $fee, string $open_id, string $patient_id, string $patient_name, Type $order_type, SourceId $source_id): mixed + { + $order_info = [ + 'relate_id' => $relate_order_id, + 'order_id' => $order_id, + 'his_order_id' => '', + 'transaction_id' => '', + 'status' => Status::NORMAL->value, + 'notify_status' => NotifyStatus::NO_ACCEPTED->value, + 'type' => $order_type, + 'pay_type' => $pay_type, + 'pay_mode' => PayMode::REFUND->value, + 'fee' => $fee, //分 + 'self_fee' => 0, //分 + 'refund_fee' => 0, + 'open_id' => $open_id, + 'patient_id' => $patient_id, + 'patient_name' => $patient_name, + 'source_id' => $source_id, + ]; + + return $this->create($order_info); + } + + /** + * 订单确认 + * @param string $order_id + * @param string $his_order_id + * @param array $response + */ + public function orderConfirm(string $order_id, string $his_order_id = '', array $response = []): void + { + $order = $this->where('order_id', $order_id)->first(); + + !empty($his_order_id) && $order->his_order_id = $his_order_id; + $order->status = Status::SUCCESS->value; + $order->save(); + + if (in_array($order->type, [Type::TODAY_REGISTRATION->value, Type::APPOINTMENT_REGISTRATION->value, Type::OUTPATIENT_PAYMENT->value]) && !empty($response)) { + + switch ($order->type) { + case Type::TODAY_REGISTRATION->value: + case Type::APPOINTMENT_REGISTRATION->value: + $record = $order->registrationRecord; + $extra_info = json_decode($record->extra_info, true); + $extra_info['confirm_response'] = $response; + + $record->update(['reg_id' => $response['AdmNo'] ?? '', 'extra_info' => json_encode($extra_info, JSON_UNESCAPED_UNICODE)]); + break; + case Type::OUTPATIENT_PAYMENT->value: + $record = $order->outpatientPaymentREcord; + $extra_info = json_decode($record->extra_info, true); + $extra_info['confirm_response'] = $response; + + $record->update(['extra_info' => json_encode($extra_info, JSON_UNESCAPED_UNICODE)]); + break; + default: + break; + } + } + } + + /** + * 修改订单状态 + * @param string $order_id + * @param Status $status + */ + public function changeOrderStatus(string $order_id, Status $status): void + { + $order = $this->where('order_id', $order_id)->first(); + $order->status = $status; + $order->save(); + } + + /** + * 订单异常(用户交钱成功,his业务确认异常 + * @param string $order_id + */ + public function abnormalOrderOpera(string $order_id): void + { + $order = $this->where('order_id', $order_id)->first(); + $order->status = Status::ABNORMAL->value; + $order->save(); + } + + /** + * 订单冲正(用户交钱成功,his业务确认失败) + * @param string $order_id + * @param float $refund_fee 此处是元 + * @param bool $is_success 退款是否成功 + */ + public function reverseOrderOpera(string $order_id, float $refund_fee, bool $is_success): void + { + $r_order = $this->where('order_id', $order_id)->first(); + + //原订单 + if ($is_success) { + $order = $this->where('id', $r_order->relate_id)->first(); + $order->refund_fee += $refund_fee * 100; + $order->status = Status::REVERSE->value; + $order->save(); + } + + // 保存退费/冲正订单 + $r_order->status = $is_success ? Status::SUCCESS->value : Status::FAILURE->value; + $r_order->refunded_at = date('Y-m-d H:i:s'); + $r_order->save(); + } + + /** + * 挂号订单解锁 + * @param string $order_id + * @return void + */ + public function regOrderUnlock(string $order_id): void + { + $order = $this->where('order_id', $order_id)->with('registrationRecord')->first(); + + $order->registrationRecord->lock_status = 2; + $order->registrationRecord->unlock_at = date('Y-m-d H:i:s'); + $order->registrationRecord->save(); + + $order->status = Status::FAILURE->value; + $order->save(); + } + + /** + * 订单取消预结算 + * @param string $order_id + * @return void + */ + public function outpatientOderCancelPreSettle(string $order_id): void + { + $order = $this->where('order_id', $order_id)->with('outpatientPaymentRecord')->first(); + + $order->outpatientPaymentRecord->pre_settle_status = 2; + $order->outpatientPaymentRecord->cancel_pre_settle_at = date('Y-m-d H:i:s'); + $order->outpatientPaymentRecord->save(); + + $order->status = Status::FAILURE->value; + $order->save(); + } + + /** + * 操作订单冲正 + * @param string $order_id + * @param int $order_type + * @param string $patient_id + * @param string $patient_name + * @param PayType $pay_type + * @param float $fee + * @param array $pay_params + * @param array $pay_res_data + * @return string + */ + public function handleOrderReverse(string $order_id, int $order_type, string $patient_id, string $patient_name, PayType $pay_type, float $fee, array $pay_params, array $pay_res_data) + { + $relate_order_info = $this->getOrderInfoByOrderId($order_id); + + // 创建冲正订单 + $r_order_id = $this->getRefundOrderId($order_id); + $this->createRefundOReverseOrder($relate_order_info['id'], $r_order_id, $pay_type, $fee * 100, $patient_id, $patient_name, $order_type); + + // 退款 + $refund_res = fastOrderRefund($pay_type, $order_id, $r_order_id, $fee, $pay_params, $pay_res_data); + $this->addLog('order reverse', '订单冲正', $refund_res); + + // 冲正失败 + if (!$refund_res[0]) { + $this->reverseOrderOpera($r_order_id, $fee, false); + return '冲正失败,失败原因:'. $refund_res[1]. ',请前往人工窗口进行退款!'; + } + + $this->reverseOrderOpera($r_order_id, $fee, true); + return '冲正成功,请稍后再行尝试!'; + } +} diff --git a/app/Models/OutpatientPaymentRecord.php b/app/Models/OutpatientPaymentRecord.php new file mode 100644 index 0000000..ead73f6 --- /dev/null +++ b/app/Models/OutpatientPaymentRecord.php @@ -0,0 +1,62 @@ + + */ + protected $fillable = [ + 'relate_order_id', + 'relate_patient_id', + 'dept_id', + 'dept_name', + 'doctor_id', + 'doctor_name', + 'visit_date', + 'total_amount', + 'pre_settle_status', + 'pre_settle_at', + 'cancel_pre_settle_at', + 'extra_info', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'id' => 'integer', + 'relate_order_id' => 'integer', + 'relate_patient_id' => 'integer', + 'total_amount' => 'integer', + ]; + + /** + * Relationships Order. + */ + public function order(): BelongsTo + { + return $this->belongsTo(Order::class, 'relate_order_id'); + } + + /** + * Relationships Patient. + */ + public function patient(): BelongsTo + { + return $this->belongsTo(Patient::class, 'relate_patient_id'); + } +} diff --git a/app/Models/Patient.php b/app/Models/Patient.php new file mode 100644 index 0000000..59c918e --- /dev/null +++ b/app/Models/Patient.php @@ -0,0 +1,199 @@ + + */ + protected $fillable = [ + 'union_id', + 'open_id', + 'patient_id', + 'card_type', + 'card_no', + 'name', + 'sex', + 'birthday', + 'mobile_phone', + 'address', + 'qr_code_text', + 'health_card_id', + 'health_card_status', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'id' => 'integer', + 'card_type' => 'integer', + 'sex' => 'integer', + 'health_card_status' => 'integer', + ]; + + /** + * Relationships Order. + */ + public function order(): HasMany + { + return $this->hasMany(Order::class, 'patient_id'); + } + + /** + * Relationships RegistrationRecord. + */ + public function registrationRecord(): HasMany + { + return $this->hasMany(Order::class, 'relate_patient_id'); + } + + /** + * Relationships outpatientPaymentRecord. + */ + public function outpatientPaymentRecord(): HasMany + { + return $this->hasMany(Order::class, 'relate_patient_id'); + } + + /** + * 获取绑定患者数量 + * @param string $open_id + * @return mixed + */ + public function getBindPatientCount(string $open_id): mixed + { + return $this->where('open_id', $open_id)->count(); + } + + /** + * 获取患者列表 + * @param string $open_id + * @return mixed + */ + public function getBindPatientLists(string $open_id): mixed + { + return $this->where('open_id', $open_id)->orderByDesc('def_status')->get(); + } + + /** + * 获取默认患者数据 + * @param string $open_id + * @return mixed + */ + public function getBindDefaultPatientInfo(string $open_id): mixed + { + return $this->where('open_id', $open_id)->orderByDesc('def_status')->first(); + } + + /** + * 获取绑定患者信息 + * @param string $open_id + * @param string $patient_id + * @return mixed + */ + public function getBindPatientInfo(string $open_id, string $patient_id): mixed + { + return $this->where('open_id', $open_id)->where('patient_id', $patient_id)->first(); + } + + /** + * 获取绑定患者信息By Id + * @param string $open_id + * @param string $patient_id + * @return mixed + */ + public function getBindPatientInfoByPatientId(string $open_id, string $patient_id): mixed + { + return $this->where('open_id', $open_id)->where('patient_id', $patient_id)->first(); + } + + /** + * 获取绑定患者信息By Patient Id + * @param string $patient_id + * @return mixed + */ + public function getPatientInfoByPatientId(string $patient_id): mixed + { + return $this->where('patient_id', $patient_id)->first(); + } + + + /** + * 建档 + * @param string $union_id + * @param string $open_id + * @param string $patient_id + * @param string $name + * @param Sex $gender + * @return mixed + */ + public function createPatient(string $union_id, string $open_id, string $patient_id, string $name, Sex $gender): mixed + { + $data = [ + 'union_id' => $union_id, + 'open_id' => $open_id, + 'patient_id' => $patient_id, + 'card_type' => 0, + 'card_no' => '', + 'name' => $name, + 'sex' => $gender->value, + 'birthday' => '', + 'mobile_phone' => '', + 'address' => '', + 'def_status' => 0 + ]; + + $count = $this->where('open_id', $open_id)->where('def_status', 1)->count(); + if (!$count) { + $data['def_status'] = 1; + } + + return $this->create($data); + } + + /** + * 设置默认 + * @param string $open_id + * @param string $patient_id + * @return bool + */ + public function setDefaultPatient(string $open_id, string $patient_id): bool + { + $this->where('open_id', $open_id)->update(['def_status' => 0]); + $res = $this->where('open_id', $open_id)->where('patient_id', $patient_id)->update(['def_status' => 1]); + + return !!$res; + } + + /** + * 删卡 + * @param string $open_id + * @param string $patient_id + * @return integer + */ + public function deletePatient(string $open_id, string $patient_id): int + { + return $this->where('open_id', $open_id)->where('patient_id', $patient_id)->delete(); + } +} diff --git a/app/Models/PushWechatMessage.php b/app/Models/PushWechatMessage.php new file mode 100644 index 0000000..7ae8d52 --- /dev/null +++ b/app/Models/PushWechatMessage.php @@ -0,0 +1,91 @@ + + */ + protected $fillable = [ + 'relate_order_id', + 'relate_patient_id', + 'type', + 'template_id', + 'scene', + 'content', + 'number', + 'status', + 'sent_at', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'id' => 'integer', + 'relate_order_id' => 'integer', + 'relate_patient_id' => 'integer', + 'number' => 'integer', + 'status' => 'integer', + 'sent_at' => 'datetime', + ]; + + /** + * Relationships Order. + */ + public function order(): BelongsTo + { + return $this->belongsTo(Order::class, 'relate_order_id'); + } + + /** + * Relationships Patient. + */ + public function patient(): BelongsTo + { + return $this->belongsTo(Patient::class, 'relate_patient_id'); + } + + /** + * 插入推送消息队列 + * @param int $relate_order_id + * @param int $relate_patient_id + * @param TemplateId|SubscribeId|null $template_id + * @param TemplateMessage|SubscribeMessage|CustomMessage $message + * @return mixed + */ + public function insertMessageJobs(int $relate_order_id, int $relate_patient_id, TemplateId|SubscribeId|null $template_id, TemplateMessage|SubscribeMessage|CustomMessage $message): mixed + { + $data = [ + 'relate_order_id' => $relate_order_id, + 'relate_patient_id' => $relate_patient_id, + 'type' => Type::OFFICIAL_TEMPLATE->value, + 'template_id' => $template_id, + 'scene' => '', + 'content' => serialize($message), + ]; + + return $this->create($data); + } +} diff --git a/app/Models/RegistrationRecord.php b/app/Models/RegistrationRecord.php new file mode 100644 index 0000000..0f2468c --- /dev/null +++ b/app/Models/RegistrationRecord.php @@ -0,0 +1,75 @@ + + */ + protected $fillable = [ + 'relate_order_id', + 'relate_patient_id', + 'reg_id', + 'dept_id', + 'dept_name', + 'dept_location', + 'doctor_id', + 'doctor_name', + 'visit_date', + 'begin_time', + 'end_time', + 'lock_status', + 'lock_at', + 'extra_info', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'id' => 'integer', + 'relate_order_id' => 'integer', + 'relate_patient_id' => 'integer', + 'lock_status' => 'integer', + 'lock_at' => 'datetime', + ]; + + /** + * Relationships Order. + */ + public function order(): BelongsTo + { + return $this->belongsTo(Order::class, 'relate_order_id'); + } + + /** + * Relationships Patient. + */ + public function patient(): BelongsTo + { + return $this->belongsTo(Patient::class, 'relate_patient_id'); + } + + /** + * 获取挂号记录by reg_id + * @param string $reg_id + * @return mixed + */ + public function getRecordByRegID(string $reg_id): mixed + { + return $this->where('reg_id', $reg_id)->first(); + } +} diff --git a/app/Models/User.php b/app/Models/User.php new file mode 100644 index 0000000..0b250cb --- /dev/null +++ b/app/Models/User.php @@ -0,0 +1,49 @@ + */ + use HasApiTokens, HasFactory, Notifiable; + + /** + * The attributes that are mass assignable. + * + * @var list + */ + protected $fillable = [ + 'name', + 'email', + 'password', + ]; + + /** + * The attributes that should be hidden for serialization. + * + * @var list + */ + protected $hidden = [ + 'password', + 'remember_token', + ]; + + /** + * Get the attributes that should be cast. + * + * @return array + */ + protected function casts(): array + { + return [ + 'email_verified_at' => 'datetime', + 'password' => 'hashed', + ]; + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php new file mode 100644 index 0000000..3c9efab --- /dev/null +++ b/app/Providers/AppServiceProvider.php @@ -0,0 +1,62 @@ +cross(); + $this->registerHisService(); + } + + /** + * Bootstrap any application services. + * + * @param UrlGenerator $url + * @return void + */ + public function boot(UrlGenerator $url): void + { + Schema::defaultStringLength(191); + + // Route参数表正则绑定 + Route::pattern('patient_id', '[0-9]+'); + Route::pattern('serial_no', '[0-9]+'); + + // 配置https + if(env('REDIRECT_HTTPS')) { + $url->forceScheme('https'); + } + } + + /** + * 允许跨域 + * @return void + */ + protected function cross(): void + { + header("Access-Control-Allow-Origin: *"); + header("Access-Control-Allow-Methods: GET,PUT,POST,PATCH,DELETE"); + header('Access-Control-Allow-Headers:x-requested-with,content-type,Authorization'); + } + + protected function registerHisService(): void + { + // His平台服务 + $this->app->singleton('HisSoapService', function () { + return new Client(); + }); + } +} diff --git a/app/Services/HisSoap/Client.php b/app/Services/HisSoap/Client.php new file mode 100644 index 0000000..ed565bf --- /dev/null +++ b/app/Services/HisSoap/Client.php @@ -0,0 +1,393 @@ +service = ClientFactory::getClientTransfer($his_name); + $this->setChannel($his_name); + } + + /** + * 请求 + * @param string $class_name + * @param string $method_name + * @param array $params + * @return mixed + * @throws GeneralException + */ + protected function requestHandle(string $class_name, string $method_name, array $params): mixed + { + try { + return $this->service->transferClass($class_name) + ->transferMethod($method_name, array_values($params)) + ->getResult(); + + } catch (Exception $e) { + $err_msg = "{$e->getMessage()} ON {$e->getFile()}:{$e->getLine()}"; + $this->error('调用soap接口失败, 错误消息:' . $err_msg, $params); + + throw new GeneralException($e->getMessage(), Response::HTTP_SERVICE_UNAVAILABLE); + } + } + + // Patient Module Start + + /** + * 建档 + * @param string $card_no + * @param CardType $card_type + * @param string $patient_name + * @param Sex $sex + * @param string $birthday + * @param string $id_card_no + * @param string $mobile + * @param string $address + * @return mixed + * @throws GeneralException + */ + public function registerCard(string $card_no, CardType $card_type, string $patient_name, Sex $sex, string $birthday, string $id_card_no, string $mobile, string $address): mixed + { + return $this->requestHandle('Create', 'CreateCardPatInfo', [ + 'CARDNO' => $card_no, + 'CARDTYPE' => $card_type->value, + 'PATIENTNAME' => $patient_name, + 'SEX' => $sex->value, + 'BIRTHDAY' => $birthday, + 'IDCARDNO' => $id_card_no, + 'MOBILE' => $mobile, + 'ADDRESS' => $address, + 'USERID' => $this->user_id, + ]); + } + + /** + * 获取患者信息 + * @param string $register_area 挂号区域(默认为 01) + * @param string $card_no 卡号 + * @param CardType $card_type 卡类型 + * @param string $name 姓名 + * @return mixed + * @throws GeneralException + */ + public function getPatientInfo(string $register_area, string $card_no, CardType $card_type, string $name): mixed + { + // 默认挂号区域为 "01",如果提供了挂号区域,则使用提供的区域 + $register_area = empty($register_area) ? '01' : $register_area; + + // 调用请求处理方法 + return $this->requestHandle('Get', 'GetCardInfo', [ + 'REGISTERAREA' => $register_area, + 'CARDNO' => $card_no, + 'CARDTYPE' => $card_type->value, + 'NAME' => $name, + 'USERID' => $this->user_id, + ]); + } + + // Patient Module End + + // Registration Module Start + + /** + * 获取科室信息(校验身份证有效卡) + * @param string $type_id 科室大类编码(如 1 普通门诊,2 急诊门诊,3 专家门诊) + * @param string $type_name 科室大类名称(如 普通门诊,急诊门诊,专家门诊) + * @param string $register_area 挂号区域(默认为 01) + * @param string $date 挂号日期(格式为 yyyy-mm-dd) + * @param string $rank_id 班次(如 1 上午,2 下午,3 晚上,4 中午) + * @return mixed + * @throws GeneralException + */ + public function getDepLists( + string $type_id, + string $type_name, + string $register_area = '01', // 默认挂号区域为 01 + string $date = '', // 默认日期为空,表示查询所有科室 + string $rank_id = '' // 默认班次为空,表示查询所有班次 + ): mixed { + // 调用请求处理方法 + return $this->requestHandle('Get', 'GetDepType', [ + 'TYPEID' => $type_id, + 'TYPENAME' => $type_name, + 'REGISTERAREA' => $register_area, + 'USERID' => $this->user_id, + 'DATE' => $date, + 'RANKID' => $rank_id, + ]); + } + + /** + * 查询医生排班信息 + * @param string $dept_id 科室编码 + * @param string $is_today 是否返回预约号源(0:当天挂号,1:返回预约明细号源,2:返回预约总号源,3:当天挂号加科室排班信息,4:返回当天挂号总号源,5:返回科室和医生排班信息) + * @param string $rank_id 班次(1:上午,2:下午,3:晚上,4:中午,默认返回所有班次) + * @param string $date 排班日期(格式为 yyyy-mm-dd,为空则返回当天以后的排班) + * @return mixed + * @throws GeneralException + */ + public function getDoctorLists( + string $dept_id, + string $is_today = '0', // 默认返回当天挂号 + string $rank_id = '', // 默认返回所有班次 + string $date = '' // 默认返回当天及以后的排班 + ): mixed { + // 调用请求处理方法 + return $this->requestHandle('Get', 'GetDoctList', [ + 'DEPID' => $dept_id, + 'ISTODAYREGIST' => $is_today, + 'RANKID' => $rank_id, + 'DATE' => $date, + 'USERID' => $this->user_id, + ]); + } + + /** + * 查询挂号记录便于就诊 + * @param string $patient_id 患者ID + * @param string $start_date 挂号开始日期,默认为空表示从今天起之后的所有挂号记录 + * @param string $end_date 挂号结束日期,默认为空表示等于开始日期 + * @param string $type 查询类型(1:当天挂号,2:预约取号),默认为空表示查询所有 + * @return mixed + * @throws GeneralException + */ + public function getRegisterRecordLists( + string $patient_id, + string $start_date = '', // 可为空,表示从今天起之后的所有挂号记录 + string $end_date = '', // 可为空,默认等于开始日期 + string $type = '' // 可为空,默认查询所有类型 + ): mixed { + return $this->requestHandle('Get', 'GetGHMXList', [ + 'PATIENTID' => $patient_id, + 'DATE' => $start_date, + 'EDATE' => $end_date, + 'TYPE' => $type, + 'USERID' => $this->user_id, + ]); + } + + + /** + * 检查挂号是否可以取消 + * @param string $visit_no 挂号流水号 + * @return mixed + * @throws GeneralException + */ + public function checkRefundRegisterStatus(string $visit_no): mixed + { + // 调用请求处理方法 + return $this->requestHandle('GH', 'GHCancelCheck', [ + 'VISITNO' => $visit_no, + 'USERID' => $this->user_id, // 假设用户 ID 来自当前实例 + ]); + } + + /** + * 确认挂号取消(退号) + * @param string $visit_no 挂号流水号 + * @param string $order_id 终端号(订单号) + * @param string $bank_tran_date 银行交易日期(格式:yyyy-mm-dd) + * @param string $bank_tran_time 银行交易时间(格式:hh24:mi:ss) + * @param string $bank_tran_amt 银行交易金额 + * @return mixed + * @throws GeneralException + */ + public function refundRegister( + string $visit_no, + string $order_id, + string $bank_tran_date, + string $bank_tran_time, + string $bank_tran_amt + ): mixed { + return $this->requestHandle('GH', 'GHCancelConfirm', [ + 'VISITNO' => $visit_no, + 'ORDERID' => $order_id, + 'BANKTRANDATE' => $bank_tran_date, + 'BANKTRANTIME' => $bank_tran_time, + 'BANKTRANAMT' => $bank_tran_amt, + 'USERID' => $this->user_id, + ]); + } + + // Registration Module End + + // Outpatient Module Start + + /** + * 获取待缴费列表 + * @param string $patient_id + * @return mixed + * @throws GeneralException + */ + public function getPendingLists(string $patient_id): mixed + { + return $this->requestHandle('List', 'ListVisitRec ', [ + 'PATIENTID' => $patient_id, + 'USERID' => $this->user_id, + ]); + } + + /** + * 查询就诊记录中的所有诊疗单据 + * @param string $cf_ids 处方号,多个处方号使用逗号分隔 + * @param string $jz_xh 就诊序号,必填 + * @param string $user_id 自助设备编码,必填 + * @param string $reg_id 号源编码,可选 + * @return mixed + * @throws GeneralException + */ + public function getPendingDetails( + string $cf_ids, + string $jz_xh, + string $user_id, + string $reg_id = '' + ): mixed { + // 调用请求处理方法 + return $this->requestHandle('List', 'ListRecipe', [ + 'CFID' => $cf_ids, + 'JZXH' => $jz_xh, + 'USERID' => $user_id, + 'REGID' => $reg_id, + ]); + } + + /** + * 获取门诊费用清单列表 + * @param string $patient_id 患者ID,必填 + * @param string $begin_date 开始日期,默认为当天(格式:yyyy-mm-dd) + * @param string $end_date 结束日期,默认为当天(格式:yyyy-mm-dd) + * @return mixed + * @throws GeneralException + */ + public function getPaidLists( + string $patient_id, + string $begin_date = '', // 可为空,默认为当天 + string $end_date = '' // 可为空,默认为当天 + ): mixed { + return $this->requestHandle('Outpatient', 'OutpatientExpenseRecord', [ + 'PATIENTID' => $patient_id, + 'BEGINDATE' => $begin_date, + 'ENDDATE' => $end_date, + ]); + } + + /** + * 获取门诊费用清单详情 + * @param string $receipt_id + * @return mixed + * @throws GeneralException + */ + public function getPaidDetails(string $receipt_id): mixed { + return $this->requestHandle('Outpatient', 'OutpatientDetailRecord', [ + 'Rcptid' => $receipt_id + ]); + } + + // Outpatient Module End + + // Electron Module Start + + + /** + * 主动调用接口生成电子发票 + * @param string $trea_id 就诊编码(结算序号) + * @return mixed + * @throws GeneralException + */ + public function createElectronInvoice(string $trea_id) + { + return $this->requestHandle('Create', 'CreateOutpatientinvoiceEBill', [ + 'TREAID' => $trea_id + ]); + } + + /** + * 发送电子票据信息给his + * @param string $treat_id 就诊编码(结算序号),必填 + * @param string $bill_batch_code 电子票据代码,必填 + * @param string $bill_no 电子票据号码,必填 + * @param string $random 电子校验码,必填 + * @param string $create_time 电子票据生成时间,格式:YYYYMMDDHHMMSSSSS,必填 + * @param string $bill_qr_code 电子票据二维码图片数据(BASE64编码),必填 + * @param string $picture_url 电子票据H5页面URL,必填 + * @param string $picture_net_url 电子票据外网H5页面URL,必填 + * @param string $wx_card_url 微信插卡URL,必填 + * @param string $bus_no 业务流水号(机构内部唯一),必填 + * @return mixed + * @throws GeneralException + */ + public function sendElectronInvoiceToHis( + string $treat_id, + string $bill_batch_code, + string $bill_no, + string $random, + string $create_time, + string $bill_qr_code, + string $picture_url, + string $picture_net_url, + string $wx_card_url, + string $bus_no + ): mixed { + return $this->requestHandle('Send', 'SendOutpatientinvoiceEBill', [ + 'TREAID' => $treat_id, + 'BILLBATCHCODE' => $bill_batch_code, + 'BILLNO' => $bill_no, + 'RANDOM' => $random, + 'CREATETIME' => $create_time, + 'BILLQRCODE' => $bill_qr_code, + 'PICTUREURL' => $picture_url, + 'PICTURENETURL' => $picture_net_url, + 'WXCARDURL' => $wx_card_url, + 'BUSNO' => $bus_no + ]); + } + + // Electron Module End + + // Dictionary Module Start + + /** + * 获取收费项目类别列表 + * @return mixed + * @throws GeneralException + */ + public function getDictionaryLists(): mixed + { + return $this->requestHandle('Get', 'GetDictionary', []); + } + + /** + * 获取收费项目详情 + * @param int $type_id 类别ID + * @return mixed + * @throws GeneralException + */ + public function getDictionaryDetails(int $type_id): mixed + { + return $this->requestHandle('Get', 'GetChargeList', [ + 'TYPEID' => $type_id + ]); + } + + // Dictionary Module End +} diff --git a/app/Utils/GeneralDailyLogger.php b/app/Utils/GeneralDailyLogger.php new file mode 100644 index 0000000..dbc8f8d --- /dev/null +++ b/app/Utils/GeneralDailyLogger.php @@ -0,0 +1,41 @@ + $config + * + * @return Logger + */ + public function __invoke(array $config): Logger { + $service_type = is_string($config['service_type']) ? $config['service_type'] : 'default'; + $level = $config['level'] instanceof Level ? $config['level'] : Level::Debug; + $path = sprintf(storage_path(). "/logs/%s/%s.log", $service_type, $level->toPsrLogLevel()); + $max_file = is_numeric($config['max_files']) ? (int) $config['max_files'] : 0; + + // Set Handler + $handler = new RotatingFileHandler($path, $max_file, $level); + $handler->setFormatter( + new LineFormatter( + "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n\n", + 'Y-m-d H:i:s.u', + true, + true, + true + ) + ); + + return new Logger($service_type, [$handler]); + } +} diff --git a/app/Utils/Helpers.php b/app/Utils/Helpers.php new file mode 100644 index 0000000..fe815d8 --- /dev/null +++ b/app/Utils/Helpers.php @@ -0,0 +1,781 @@ +json([ + 'message' => $msg, + 'data' => $data, + 'status_code' => $status_code + ], $status_code)->setEncodingOptions(JSON_UNESCAPED_UNICODE); + } +} + +if (!function_exists('jsonResponseToHis')) { + /** + * json返回 to his + * @param int $status_code + * @param int $code + * @param string $msg + * @param array $data + * @return JsonResponse + */ + function jsonResponseToHis(int $status_code, int $code, string $msg, array $data = []): JsonResponse + { + return response()->json([ + 'code' => $code, + 'msg' => $msg, + 'data' => $data, + ], $status_code)->setEncodingOptions(JSON_UNESCAPED_UNICODE); + } +} + +if (!function_exists('validateIDCard')) { + /** + * 校验身份证格式 + * @param string $id_card + * @return bool + */ + function validateIDCard(string $id_card): bool + { + $id_card = strtoupper($id_card); + $regx = "/(^\d{15}$)|(^\d{17}([0-9]|X)$)/"; + $arr_split = []; + + if (!preg_match($regx, $id_card)) { + return false; + } + + if (15 == strlen($id_card)) { + //检查15位 + $regx = "/^(\d{6})+(\d{2})+(\d{2})+(\d{2})+(\d{3})$/"; + @preg_match($regx, $id_card, $arr_split); + + //检查生日日期是否正确 + $dtm_birth = "19".$arr_split[2] . '/' . $arr_split[3]. '/' .$arr_split[4]; + + if (!strtotime($dtm_birth)) { + return false; + } + + return true; + } else { + //检查18位 + $regx = "/^(\d{6})+(\d{4})+(\d{2})+(\d{2})+(\d{3})([0-9]|X)$/"; + @preg_match($regx, $id_card, $arr_split); + + //检查生日日期是否正确 + $dtm_birth = $arr_split[2] . '/' . $arr_split[3]. '/' .$arr_split[4]; + + if (!strtotime($dtm_birth)) { + return false; + } else { + //检验18位身份证的校验码是否正确。 + //校验位按照ISO 7064:1983.MOD 11-2的规定生成,X可以认为是数字10。 + $arr_int = array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2); + $arr_ch = array('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'); + $sign = 0; + + for ( $i = 0; $i < 17; $i++ ) { + $b = (int) $id_card[$i]; + $w = $arr_int[$i]; + $sign += $b * $w; + } + + $n = $sign % 11; + $val_num = $arr_ch[$n]; + + if ($val_num != substr($id_card,17, 1)) { + return false; + } + + return true; + } + } + } +} + +if (!function_exists('getGenderByIdCard')) +{ + /** + * 根据 身份证 / 2023 外国人永居证 获取性别 + * @param string $id_card_no + * @return int + */ + function getGenderByIdCard(string $id_card_no): int + { + if (strlen($id_card_no) == 18) { + //18位身份证 第17位 奇男 偶女 + $number = substr($id_card_no, -2, 1); + } else { + //15位身份证 第15位 奇男 偶女 + $number = substr($id_card_no, -1, 1); + } + + //求余 + if (intval($number) % 2 !== 0) { + return 1; + } else { + return 2; + } + } +} + +if (!function_exists('validate2017ForeignersIDCard')) { + /** + * 2017 版外国人永居证号码校验 + * @param string $id_card + * @return bool + */ + function validate2017ForeignersIDCard(string $id_card): bool + { + //检查15位 + if (15 !== strlen($id_card)) { + return false; + } + + // 2017 版外国人永居证 前 3 位是大写字母 后 12 位是数字 + if (!preg_match( "/^[A-Z]{3}[0-9]{12}$/", $id_card)) { + return false; + } + + // 除最后一位外都为计算位数 + $length = 15 - 1; + // 本体码 + $local = []; + // 乘积 + $product = []; + // 731 算法,加权因子 + $widths = [7, 3, 1, 7, 3, 1, 7, 3, 1, 7, 3, 1, 7, 3]; + + for ($i = $length; $i > 0; $i--) { + // 前三位为大写字母,映射为10-35的十进制数字 + if ($i > $length - 3) { + $local[$length - $i] = mapLetterToNumber($id_card[$length - $i]); + } else { + $local[$length - $i] = intval($id_card[$length - $i]); + } + // 乘积 + $product[$length - $i] = $local[$length - $i] * $widths[$length - $i]; + } + // 乘积之和对10取模 + $modulus = array_sum($product) % 10; + // 最后一位为校验码 + $check_digit = (int) $id_card[$length]; + // 校验码与计算得到结果比对 + return $modulus == $check_digit; + } +} + +if (!function_exists('mapLetterToNumber')) { + /** + * 2017 版外国人永居证 证件前三位拉丁字母映射为 10-35的十进制数字 + * @param string $letter 单个拉丁字母 + * @return int 数字 + */ + function mapLetterToNumber(string $letter): int + { + return ord($letter) - ord('A') + 10; + } +} + +if (!function_exists('getIDCardType')) { + /** + * 判断身份证件类型 + * @param string $id_card + * @return int 0 其他, 1 身份证,2 港澳台社保卡号, 3 2017 外国人永居证,4 2023 外国人永居证 + */ + function getIDCardType(string $id_card): int + { + // 身份证 + $length = strlen($id_card); + if (($length === 18 || $length === 15) && (int) substr($id_card, 0, 1) !== 9 && validateIDCard($id_card)) { + return 1; + } + + // 港澳台 社保卡号 + if (preg_match("/^[HKG|MAC|TWN][0-9]{9}$/", $id_card)) { + return 2; + } + + // 2017 版外国人永居证 前 3 位是大写字母 后 12 位是数字 + if (preg_match("/^[A-Z]{3}[0-9]{12}$/", $id_card)) { + return 3; + } + + // 2023 版外国人永居证第一位是 9 + if ((int) substr($id_card, 0, 1) === 9) { + return 4; + } + + return 0; + } +} + +if (!function_exists('getBirthdayByIdCard')) +{ + /** + * 根据 身份证 / 2023 外国人永居证 获取出生日期 + * @param string $id_card_no + * @param string $format_rule + * @return string + */ + function getBirthdayByIdCard(string $id_card_no, string $format_rule = 'Y-m-d'): string + { + if (strlen($id_card_no) == 18) { + //18位身份证 + //第7、8、9、10位为出生年份(四位数) + //第11、第12位为出生月份 + //第13、14位代表出生日期 + $year = substr($id_card_no, 6, 4); + $month = substr($id_card_no, 10, 2); + $day = substr($id_card_no, 12, 2); + + $birthday = $year. '-'. $month. '-'. $day; + } else { + //15位身份证 + //第7、8位为出生年份(两位数) + //第9、10位为出生月份 + //第11、12位代表出生日期 + $year = substr($id_card_no, 6, 2); + $month = substr($id_card_no, 8, 2); + $day = substr($id_card_no, 10, 2); + + $birthday = '19'. $year. '-'. $month. '-'. $day; + } + + return date($format_rule, strtotime($birthday)); + } +} + + +if (!function_exists('getBirthdayBy2017ForeignersIDCard')) +{ + /** + * 根据 2017 外国人永居证 获取出生日期 + * @param string $id_card_no + * @param string $format_rule + * @return string + */ + function getBirthdayBy2017ForeignersIDCard(string $id_card_no, string $format_rule = 'Y-m-d'): string + { + // 2017年外国人永居证 + // 第8、9位为出生年份(两位数) + // 第10、11位为出生月份 + // 第12、13位代表出生日期 + $year = substr($id_card_no, 7, 2); + $month = substr($id_card_no, 8, 2); + $day = substr($id_card_no, 11, 2); + + $birthday = '19'. $year. '-'. $month. '-'. $day; + + return date($format_rule, strtotime($birthday)); + } +} + +if (!function_exists('checkMobilePhone')) { + /** + * 检测手机号码 + * @param string $mobile_phone + * @return bool + */ + function checkMobilePhone(string $mobile_phone): bool + { + $mobile_phone = trim($mobile_phone); + $regex = "/^1(3|4|5|6|7|8|9)\d{9}$/"; + + if (!preg_match($regex, $mobile_phone)) { + return false; + } + + return true; + } +} + +if (!function_exists('checkFixedTelephone')) { + /** + * 检测固定电话 + * @param string $fixed_telephone + * @return bool + */ + function checkFixedTelephone(string $fixed_telephone): bool + { + $fixed_telephone = trim($fixed_telephone); + $regex = "/^(0[0-9]{2,3})?([2-9][0-9]{6,7})+([0-9]{1,4})?$/"; + + if (!preg_match($regex, $fixed_telephone)) { + return false; + } + + return true; + } +} + +if (!function_exists('checkDateFormat')) { + /** + * 检查日期格式 + * @param string $date_str + * @param string $rules + * @return bool + */ + function checkDateFormat(string $date_str, string $rules = 'Y-m-d'): bool + { + try { + return date($rules, strtotime($date_str)) == $date_str; + } catch (\Exception $e) { + return false; + } + } +} + +if (!function_exists('xmlArrayToListByKey')) { + /** + * xml的array转成lists + * @param array $data + * @param string $key + * @return array + */ + function xmlArrayToListByKey(array $data, string $key): array + { + //判断是否存在0,1,2,3等键值数组 + if( + isset($data[$key]) && + (!isset($data[$key][0]) || reset($data[$key]) !== $data[$key][0]) + ) { + $data[$key] = [$data[$key]]; + } + + return $data; + } +} + +if (!function_exists('objectToArray')) { + /** + * object to array + * @param object $data + * @return array + */ + function objectToArray(object $data): array + { + return json_decode(json_encode($data), true); + } +} + +if (!function_exists('recordLog')) { + /** + * 保存日志记录 + * @param string $module_name + * @param string $content + */ + function recordLog(string $module_name, string $content): void + { + date_default_timezone_set("Asia/Shanghai"); + + $file_path = app()->storagePath('logs'). DIRECTORY_SEPARATOR. $module_name. 'Log'. DIRECTORY_SEPARATOR. date('Ym'). DIRECTORY_SEPARATOR; + $file_name = date('d'). '.log'; + + !is_dir($file_path) && mkdir($file_path, 0755, true); + + $msg = '['. date('Y-m-d H:i:s'). ']'. PHP_EOL . $content . PHP_EOL . PHP_EOL; + + file_put_contents( $file_path. $file_name, $msg, FILE_APPEND); + } +} + +if (!function_exists('getCustomConfig')) { + /** + * 获取自定义配置 + * @param string $name + * @param string $key + * @return mixed + */ + function getCustomConfig(string $name, string $key): mixed + { + $config = config('custom.'. $name); + return isset($config[$key]) ? $config[$key] : reset($config); + } +} + +if (!function_exists('getMonthDateList')) { + /** + * 获取当月列表 + * @return array + */ + function getMonthDateList(): array + { + $lists = []; + $first_day = strtotime(date('Y-m-01')); + $i = 0; + $last_day = strtotime(date('Y-m-01'). ' +1 month'); + + while ($first_day + $i * 86400 < $last_day) { + $lists[] = date('Y-m-d', $first_day + $i * 86400); + $i++; + } + + return $lists; + } +} + +if (!function_exists('arrayToXml')) { + /** + * 数组转xml + * @param array $data + * @return string + */ + function arrayToXml(array $data): string + { + $xml = ''; + foreach ($data as $key => $val) + { + if (is_array($val)) { + $xml .= "<" . $key . ">" . arrayToXml($val) . ""; + } else { + $xml .= "<" . $key . ">" . $val . ""; + } + } + + return $xml; + } +} + +if (!function_exists('getHisPayTypeByCustomPayType')) { + /** + * 获取his支付类型 + * @param int $pay_type 自定义支付类型 + * @param bool $is_staff 是否为职工挂号 + * @return int + */ + function getHisPayTypeByCustomPayType(int $pay_type, bool $is_staff): int + { + return !$is_staff ? (config('custom.pay_type_to_his')[$pay_type] ?? 0) : 4; + } +} + +if (!function_exists('getHisPayOrderNoByHisPayType')) { + /** + * 获取his支付类型 + * @param int $his_pay_type his支付类型 + * @param array $pay_res_data 是否为职工挂号 + * @return string + */ + function getHisPayOrderNoByHisPayType(int $his_pay_type, array $pay_res_data): string + { + switch ($his_pay_type) { + case 0: + case 1: + if (isset($pay_res_data['orderNo'], $pay_res_data['merchantId'])) { + // 数字人民币 + $order_no = $pay_res_data['orderNo']; + } else { + // 正常银联支付 + $order_no = $pay_res_data['retCardNo'] ?? '';// UnMoney.Str2 / retCardNo + } + break; + case 2: + case 3: + default: + $order_no = $pay_res_data['out_trade_no'] ?? ''; + break; + case 5: + if (isset($pay_res_data['hospitalSerialNo']) && strlen($pay_res_data['hospitalSerialNo']) === 20) { + // 挂号信用付 + $order_no = $pay_res_data['hospitalSerialNo']; + } else { + // 住院信用付 + $order_no = ''; + } + break; + } + + return $order_no; + } +} + +if (!function_exists('routeResource')) { + /** + * 设置资源路由 + * @param Route $router 路由handle + * @param string $uri 路由名称 + * @param string $controller 控制器名称 + * @param array $allow 允许的方法 + * @return void + */ + function routeResource(Route &$router, string $uri, string $controller, array $allow = ['index', 'store', 'show', 'update', 'destory']): void + { + if (in_array('index', $allow)) { + $router->get($uri, $controller. '@index'); + } + + if (in_array('store', $allow)) { + $router->post($uri, $controller. '@store'); + } + + if (in_array('show', $allow)) { + $router->get($uri.'/{id:[0-9]+}', $controller. '@show'); + } + + if (in_array('update', $allow)) { + $router->put($uri.'/{id:[0-9]+}', $controller. '@update'); + $router->patch($uri.'/{id:[0-9]+}', $controller. '@update'); + } + + if (in_array('destory', $allow)) { + $router->delete($uri . '/{id:[0-9]+}', $controller . '@destroy'); + } + } +} + +if (!function_exists('getUrlQueryParams')) { + /** + * 获取url参数数组 + * @param string $url + * @return array + */ + function getUrlQueryParams(string $url): array + { + $query_param_str = parse_url($url, PHP_URL_QUERY); + if (empty($query_param_str)) { + return []; + } + + $params_arr = explode('&', $query_param_str); + + $params = []; + foreach ($params_arr as $k => $v) { + $arr = explode('=', $v); + $params[$arr[0]] = $arr[1]; + } + + return $params; + } +} + +if (!function_exists('getCsvFileContent')) { + /** + * 读取csv文件 + * @param string $file_path 文件路径 + * @param int $ignore_head_line 忽略头部行数 + * @param int $ignore_foot_line 忽略脚部行数 + * @return array + */ + function getCsvFileContent(string $file_path, int $ignore_head_line = 0, int $ignore_foot_line = 0): array + { + if (!file_exists($file_path)) { + return [false, '文件不存在']; + } + + $i = 0; + $data = []; + $handle = fopen($file_path, 'r'); + while (($content = fgetcsv($handle)) !== false) { + // 忽略头部行数 + if ($i < $ignore_head_line) { + $i++; + continue; + } + + $i++; + $data[] = $content; + } + + // 忽略尾部行数 + if ($ignore_foot_line > 0) { + $data = array_chunk($data, $i - $ignore_foot_line - $ignore_head_line); + if (!is_array($data)) { + return [true, []]; + } + + $data = $data[0]; + } + + return [true, $data]; + } +} + +if (!function_exists('getFormatDateTimeStr')) { + /** + * 获取当前格式化后的时间字符串 + * @param string $format 格式化后的字符串 + * @param int $pow_num 保留几位秒数 + * @return string + */ + function getFormatDateTimeStr(string $format = 'Y-m-d H:i:s', int $pow_num = 6): string + { + date_default_timezone_set('Asia/Shanghai'); + // 带微秒的时间戳 + $u_timestamp = sprintf("%.6f", microtime(true)); + + $timestamp = floor($u_timestamp); + $microseconds = round(($u_timestamp - $timestamp) * pow(10, $pow_num)); + + return date($format, $timestamp) . $microseconds; + } +} + +if (!file_exists('getPaymentOutTradeOrderId')) { + /** + * 根据支付类型返回支付平台订单ID + * @param int $pay_type 支付类型 + * @param array $pay_result 支付返回 + * @return mixed|string + */ + function getPaymentOutTradeOrderId(int $pay_type, array $pay_result): mixed + { + switch ($pay_type) { + case 2: + case 5: + case 9: + $out_trade_id = $pay_result['retFlowWaterNo'] ?? ''; + break; + case 3: + case 6: + // 查找顺序 微信支付 -> HIS支付平台 + $out_trade_id = $pay_result['transaction_id'] ?? ($pay_result['platform_order_no'] ?? ''); + break; + case 4: + case 7: + // 查找顺序 支付宝支付 -> HIS支付平台 + $out_trade_id = $pay_result['trade_no'] ?? ($pay_result['platform_order_no'] ?? ''); + break; + case 10: + case 11: + $out_trade_id = $pay_result['orderNo'] ?? ''; + break; + case 12: + $out_trade_id = $pay_result['transNo'] ?? ''; + break; + default: + $out_trade_id = ''; + break; + } + + return $out_trade_id; + } +} + +if (!function_exists('getArrayByKeyList')) { + /** + * 根据键值列表获取数据 + * @param array $array 原数据 + * @param array $list 需要取得键值数组 + * @return array + */ + function getArrayByKeyList(array $array, array $list): array + { + if (empty($array) || empty($list)) { + return []; + } + + $n_array = []; + foreach ($list as $k => $v) { + $n_array[$v] = $array[$v] ?? ''; + } + + return $n_array; + } +} + +if (!function_exists('getArrayColumnListsByKey')) { + /** + * 根据键值获取数组列的列表 + * @param $array + * @param $key + * @return array + */ + function getArrayColumnListsByKey($array, $key): array + { + if (empty($array)) { + return []; + } + + $lists = []; + foreach ($array as $k => $v) { + if (!isset($v[$key])) { + continue; + } + + $lists[$v[$key]] = $v; + } + + return $lists; + } +} + +if (!function_exists('getElectronHealthConfig')) { + /** + * 获取电子健康卡配置 + * @param string $name + * @param string $key + * @return mixed + */ + function getElectronHealthConfig(string $name, string $key): mixed + { + $config = config('health.'. $name); + return isset($config[$key]) ? $config[$key] : reset($config); + } +} + +if (!function_exists('generateTree')) { + /** + * 分类树 + * @param array $array 分类数据 + * @param string $s_key 子类IDkey名称 + * @param string $p_key 父类IDkey名称 + * @param string $item_key 子类存储key名称 + * @return array + */ + function generateTree(array $array, string $s_key, string $p_key, string $item_key): array + { + $items = []; + foreach($array as $v){ + $items[$v[$s_key]] = $v; + } + + $tree = []; + foreach($items as $k => $v){ + if(isset($items[$v[$p_key]])){ + $items[$v[$p_key]][$item_key][] = &$items[$k]; + }else{ + $tree[] = &$items[$k]; + } + } + return $tree; + } +} + +if (!function_exists('getWeChatMiniProgramApp')) { + /** + * 获取小程序app示例 + * @return Application + * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException + */ + function getWeChatMiniProgramApp(): Application + { + return new Application(config('wechat.mini')); + } +} + +if (!function_exists('replaceSpecialChar')) { + /** + * 过滤特殊字符 + * @param string $string + * @return array|string|string[]|null + */ + function replaceSpecialChar(string $string): array|string|null + { + $regex = "-[/~!@#$%^&*()_+{}:<>?\[\],.;`'\-=|]-"; + return preg_replace($regex, '', $string); + } +} diff --git a/app/Utils/ProxyHelpers.php b/app/Utils/ProxyHelpers.php new file mode 100644 index 0000000..3b4696e --- /dev/null +++ b/app/Utils/ProxyHelpers.php @@ -0,0 +1,76 @@ +root() . '/'. $module. '/oauth/token'; + + $params = array_merge(config('passport.'. $config), [ + 'username' => $username, + 'password' => $password, + 'provider' => $guard + ]); + + $respond = $client->post($url, ['form_params' => $params]); + + } catch (RequestException $exception) { + return false; + } + + if ($respond->getStatusCode() === 401) { + return false; + } + + return json_decode($respond->getBody()->getContents(), true); + } + + /** + * 获取刷新token + * @param string $module 模块 + * @param string $config 配置 + * @return boolean|mixed + * @throws GuzzleException + */ + public function getRefreshToken(string $module, string $config): mixed + { + $client = new Client(); + + try { + $url = request()->root() . '/'. $module. '/oauth/token'; + + $params = array_merge(config('passport.'. $config), [ + 'refresh_token' => request('refresh_token'), + ]); + + $response = $client->post($url, ['form_params' => $params]); + } catch (RequestException $exception) { + return false; + } + + if ($response->getStatusCode() === 401) { + return false; + } + + return json_decode($response->getBody()->getContents(), true); + } +} diff --git a/app/Utils/SM4.php b/app/Utils/SM4.php new file mode 100644 index 0000000..0ce9ff0 --- /dev/null +++ b/app/Utils/SM4.php @@ -0,0 +1,266 @@ +key = $this->preProcess($key); + $this->setSkey(); + } + + /** + * 计算每轮加密需要的秘钥 + */ + private function setSkey(): void + { + $skey = []; + for ($i = 0; $i < 4; $i++) { + $skey[] = self::SM4_FK[$i] ^ ($this->key[4 * $i] << 24 | $this->key[4 * $i + 1] << 16 | $this->key[4 * $i + 2] << 8 | $this->key[4 * $i + 3]); + } + for ($k = 0; $k < 32; $k++) { + $tmp = $skey[$k + 1] ^ $skey[$k + 2] ^ $skey[$k + 3] ^ self::SM4_CK[$k]; + + //非线性化操作 + $buf = (self::SM4_Sbox[($tmp >> 24) & 0xff]) << 24 | + (self::SM4_Sbox[($tmp >> 16) & 0xff]) << 16 | + (self::SM4_Sbox[($tmp >> 8) & 0xff]) << 8 | + (self::SM4_Sbox[$tmp & 0xff]); + //线性化操作 + $skey[] = $skey[$k] ^ ($buf ^ $this->sm4Rotl32($buf, 13) ^ $this->sm4Rotl32($buf, 23)); + $this->skey[] = $skey[$k + 4]; + } + } + + + /** + * 32比特的buffer中循环左移n位 + * @param $buf int 可以传递进10进制 也可以是0b开头的二进制 + * @param $n int 向左偏移n位 + * @return int + * reference http://blog.csdn.net/w845695652/article/details/6522285 + */ + private function sm4Rotl32(int $buf, int $n): int + { + return (($buf << $n) & 0xffffffff) | ($buf >> (32 - $n)); + } + + /** + * 对字符串加密 + * @param string $plain_text + * @return string + * @throws Exception + */ + public function encrypt(string $plain_text): string + { + $bytes = bin2hex($plain_text); + $need_pad_length = $this->block_size - strlen($bytes) % $this->block_size; + $pad_bytes = str_pad( + $bytes, + strlen($bytes) + $need_pad_length, + sprintf("%02x", $need_pad_length / 2), + STR_PAD_RIGHT + ); + $chunks = str_split($pad_bytes, $this->block_size); + + return strtolower(implode('', array_map(function ($chunk) { + return $this->encryptBinary($chunk); + }, $chunks))); + } + + + /** + * SM4加密单个片段(128bit) + * @param $text string 32个十六进制字符串 + * @return string + * @throws Exception + */ + private function encryptBinary(string $text): string + { + $x = $re = []; + $t = $this->preProcess($text); + for ($i = 0; $i < 4; $i++) { + $x[] = $t[$i * 4] << 24 | + $t[$i * 4 + 1] << 16 | + $t[$i * 4 + 2] << 8 | + $t[$i * 4 + 3]; + } + + for ($k = 0; $k < 32; $k++) { + $tmp = $x[$k + 1] ^ $x[$k + 2] ^ $x[$k + 3] ^ $this->skey[$k]; + + $buf = self::SM4_Sbox[($tmp >> 24) & 0xff] << 24 | + self::SM4_Sbox[($tmp >> 16) & 0xff] << 16 | + self::SM4_Sbox[($tmp >> 8) & 0xff] << 8 | + self::SM4_Sbox[$tmp & 0xff]; + + $x[$k + 4] = $x[$k] ^ $buf + ^ $this->sm4Rotl32($buf, 2) + ^ $this->sm4Rotl32($buf, 10) + ^ $this->sm4Rotl32($buf, 18) + ^ $this->sm4Rotl32($buf, 24); + } + for ($i = 0; $i < 4; $i++) { + $re[] = ($x[35 - $i] >> 24) & 0xff; + $re[] = ($x[35 - $i] >> 16) & 0xff; + $re[] = ($x[35 - $i] >> 8) & 0xff; + $re[] = $x[35 - $i] & 0xff; + } + return $this->wrapResult($re); + } + + + /** + * 预处理16字节长度的16进制字符串 返回10进制的数组 数组大小为16 + * @param string $text + * @return array + * @throws Exception + */ + private function preProcess(string $text): array + { + preg_match('/[0-9a-f]{32}/', strtolower($text), $re); + if (empty($re)) { + throw new Exception('error input format!'); + } + $key = $re[0]; + for ($i = 0; $i < 16; $i++) { + $result[] = hexdec($key[2 * $i] . $key[2 * $i + 1]); + } + + return $result; + } + + /** + * 将十进制结果包装成16进制字符串输出 + * @param array $result + * @return string + */ + private function wrapResult(array $result): string + { + $hex_str = ''; + foreach ($result as $v) { + $tmp = dechex($v); + $len = strlen($tmp); + if ($len == 1) //不足两位十六进制的数 在前面补一个0,保证输出也是32个16进制字符 + { + $hex_str .= '0'; + } + $hex_str .= $tmp; + } + return strtoupper($hex_str); + } + + + /** + * SM4解密单个片段(128bits) + * @param string $text 32个16进制字符串 + * @return string + * @throws Exception + */ + private function decrypt_decrypt(string $text): string + { + $x = $re = []; + $t = $this->preProcess($text); + for ($i = 0; $i < 4; $i++) { + $x[] = $t[4 * $i] << 24 | + $t[4 * $i + 1] << 16 | + $t[4 * $i + 2] << 8 | + $t[4 * $i + 3]; + } + for ($k = 0; $k < 32; $k++) { + $tmp = $x[$k + 1] ^ $x[$k + 2] ^ $x[$k + 3] ^ $this->skey[31 - $k]; + $buf = (self::SM4_Sbox[($tmp >> 24) & 0xff]) << 24 | + (self::SM4_Sbox[($tmp >> 16) & 0xff]) << 16 | + (self::SM4_Sbox[($tmp >> 8) & 0xff]) << 8 | + (self::SM4_Sbox[$tmp & 0xff]); + $x[$k + 4] = $x[$k] ^ $buf + ^ $this->sm4Rotl32($buf, 2) + ^ $this->sm4Rotl32($buf, 10) + ^ $this->sm4Rotl32($buf, 18) + ^ $this->sm4Rotl32($buf, 24); + } + + for ($i = 0; $i < 4; $i++) { + $re[] = ($x[35 - $i] >> 24) & 0xff; + $re[] = ($x[35 - $i] >> 16) & 0xff; + $re[] = ($x[35 - $i] >> 8) & 0xff; + $re[] = $x[35 - $i] & 0xff; + } + return $this->wrapResult($re); + } + + /** + * 字符串解密 + * @param string $cipher_text + * @return string + * @throws Exception + */ + public function decrypt(string $cipher_text): string + { + $chunks = str_split($cipher_text, $this->block_size); + $decrypt_text_data = implode('', array_map(function ($chunk) { + return $this->decrypt_decrypt($chunk); + }, $chunks)); + + $pad_length = hexdec(substr($decrypt_text_data, -2)); + + return hex2bin(preg_replace( + sprintf("/%s$/", str_repeat(sprintf("%02x", $pad_length), $pad_length)), + '', + $decrypt_text_data + )); + } +} diff --git a/app/Utils/Traits/HttpRequest.php b/app/Utils/Traits/HttpRequest.php new file mode 100644 index 0000000..a924fa8 --- /dev/null +++ b/app/Utils/Traits/HttpRequest.php @@ -0,0 +1,265 @@ + + */ + private array $httpHeader = []; + + /** + * Http client options. + * + * @var array + */ + protected array $httpOptions = [ + 'base_uri' => '', + 'timeout' => 0, + 'connect_timeout' => 0, + ]; + + /** + * Send a GET request. + * + * @param string $endpoint 请求路由 + * @param array $query GET参数 + * @param array $headers 请求header头 + * + * @return mixed + * + * @throws JsonException + */ + public function get(string $endpoint, array $query = [], array $headers = []): mixed + { + return $this->request('GET', $endpoint, [ + 'headers' => $headers, + 'query' => $query, + ]); + } + + /** + * Send a POST request. + * + * @param string $endpoint 请求路由 + * @param string|array $data 请求数据 + * @param array $options options选项 + * + * @return mixed + * @throws JsonException + */ + public function post(string $endpoint, string|array $data, array $options = []): mixed + { + if (!is_array($data)) { + $options['body'] = $data; + } else { + $options['form_params'] = $data; + } + + return $this->request('POST', $endpoint, $options); + } + + /** + * Send request. + * + * @param string $method 请求方法 + * @param string $endpoint 请求路由 + * @param array $options options选项 + * + * @return mixed + * + * @throws JsonException + */ + public function request(string $method, string $endpoint, array $options = []): mixed + { + return $this->unwrapResponse($this->getHttpClient()->{$method}($endpoint, $options)); + } + + /** + * Convert response. + * + * @param ResponseInterface $response 返回的response对象 + * + * @return mixed + * + * @throws JsonException + */ + public function unwrapResponse(ResponseInterface $response): mixed + { + $contentType = $response->getHeaderLine('Content-Type'); + $contents = $response->getBody()->getContents(); + + if (false !== stripos($contentType, 'json') || stripos($contentType, 'javascript')) { + return json_decode($contents, true, 512, JSON_THROW_ON_ERROR); + } + + if (false !== stripos($contentType, 'xml')) { + return json_decode( + json_encode( + simplexml_load_string($contents, 'SimpleXMLElement', LIBXML_NOCDATA), + JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE + ), + true, + 512, + JSON_THROW_ON_ERROR + ); + } + + return $contents; + } + + /** + * Set http client. + * + * @param Client $client client请求客户端 + * + * @return self + */ + public function setHttpClient(Client $client): self + { + $this->httpClient = $client; + + return $this; + } + + /** + * Get http client. + * + * @return Client + */ + public function getHttpClient(): Client + { + if (is_null($this->httpClient)) { + $this->httpClient = $this->getDefaultHttpClient(); + } + + return $this->httpClient; + } + + /** + * Get default http client. + * + * @return Client + */ + public function getDefaultHttpClient(): Client + { + return new Client($this->getOptions()); + } + + /** + * set mock Handler + * + * @param array $mock + * + * @return self + */ + public function setMockHandler(array $mock): self + { + array_walk($mock, static function ($value) { + if (!is_subclass_of($value, ResponseInterface::class)) { + throw new InvalidArgumentException( + $value::class . ' must be an instance of ' . ResponseInterface::class + ); + } + }); + + $this->mockHandler = new MockHandler($mock); + $handlerStack = HandlerStack::create($this->mockHandler); + $this->setHttpOptions(['handler' => $handlerStack]); + + return $this; + } + + /** + * Get mock handler + * + * @return MockHandler|null + */ + public function getMockHandler(): ?MockHandler + { + return $this->mockHandler; + } + + /** + * Get default options. + * + * @return array + */ + public function getOptions(): array + { + return $this->getHttpOptions(); + } + + /** + * Set http header. + * + * @param array $httpHeader + * + * @return self + */ + public function setHttpHeader(array $httpHeader): self + { + $this->httpHeader = array_merge($httpHeader, $this->httpHeader); + + return $this; + } + + /** + * Get http header. + * + * @return array + */ + public function getHttpHeader(): array + { + return $this->httpHeader; + } + + /** + * Set http options. + * + * @param array $httpOptions + * + * @return void + */ + public function setHttpOptions(array $httpOptions): void + { + $this->httpOptions = array_merge($this->httpOptions, $httpOptions); + } + + /** + * Get http options. + * + * @return array + */ + public function getHttpOptions(): array + { + return $this->httpOptions; + } +} diff --git a/app/Utils/Traits/Logger.php b/app/Utils/Traits/Logger.php new file mode 100644 index 0000000..ed81a39 --- /dev/null +++ b/app/Utils/Traits/Logger.php @@ -0,0 +1,114 @@ +logger)) { + $this->logger = $this->createLogger(); + $this->uuid = $this->uuid(); + } + + return $this->logger; + } + + /** + * Create Logger. + * + * @return LoggerInterface + */ + public function createLogger(): LoggerInterface + { + return Log::channel($this->channel); + } + + /** + * Set Channel. + * + * @param string $channel + * + * @return void + */ + public function setChannel(string $channel): void + { + $this->channel = $channel; + } + + /** + * Create uuid + * @return string + */ + protected function uuid(): string + { + $chars = md5(uniqid((string)mt_rand(), true)); + return substr ( $chars, 0, 8 ) . '-' + . substr ( $chars, 8, 4 ) . '-' + . substr ( $chars, 12, 4 ) . '-' + . substr ( $chars, 16, 4 ) . '-' + . substr ( $chars, 20, 12 ); + } + + public function debug(string $message, array $context = []): void + { + $this->getLogger()->debug('['. $this->uuid. ']'.$message, $context); + } + + + public function info(string $message, array $context = []): void + { + $this->getLogger()->info('['. $this->uuid. ']'.$message, $context); + } + + + public function notice(string $message, array $context = []): void + { + $this->getLogger()->notice('['. $this->uuid. ']'.$message, $context); + } + + + public function warning(string $message, array $context = []): void + { + $this->getLogger()->warning('['. $this->uuid. ']'.$message, $context); + } + + + public function error(string $message, array $context = []): void + { + $this->getLogger()->error('['. $this->uuid. ']'.$message, $context); + } + +} diff --git a/app/Utils/Traits/MiniProgramAuth.php b/app/Utils/Traits/MiniProgramAuth.php new file mode 100644 index 0000000..e46215c --- /dev/null +++ b/app/Utils/Traits/MiniProgramAuth.php @@ -0,0 +1,36 @@ +header('authorization'); + $info = Redis::get(substr($token, 7)); + + if(empty($info)) { + throw new AuthenticationException('Unauthenticated.'); + } + + $info = json_decode($info, true); + $this->open_id = $info['open_id'] ?? ''; + $this->union_id = $info['union_id'] ?? ''; + $this->session_key = $info['session_key'] ?? ''; + } +} diff --git a/app/Utils/Traits/RedisLockUtil.php b/app/Utils/Traits/RedisLockUtil.php new file mode 100644 index 0000000..1d2f9bf --- /dev/null +++ b/app/Utils/Traits/RedisLockUtil.php @@ -0,0 +1,55 @@ +getUniqueLockId(); + $result = $redis->set($lock_str, $unique_id, 'ex', $expire_time, 'nx'); + + return $result ? $unique_id : false; + } + /** + * 解锁 + * @param string $lock_str 锁字符串 + * @param string $unique_id 唯一锁ID + * @return bool + * @throws RedisException + */ + public function unlock(string $lock_str, string $unique_id): bool + { + $redis = app('redis'); + + $redis->watch($lock_str); + if ($unique_id == $redis->get($lock_str)) { + $redis->multi()->del($lock_str)->exec(); + return true; + } + $redis->unwatch(); + return false; + } + + /** + * 获取唯一锁ID + * @return string + */ + protected function getUniqueLockId(): string + { + return md5(uniqid((string)mt_rand(), true)); + } +} diff --git a/app/Utils/Traits/UniversalEncryption.php b/app/Utils/Traits/UniversalEncryption.php new file mode 100644 index 0000000..e5a4a56 --- /dev/null +++ b/app/Utils/Traits/UniversalEncryption.php @@ -0,0 +1,48 @@ +getKey(), OPENSSL_RAW_DATA, $iv); + + $encryptedWithIv = $iv . $encrypted; + + return base64_encode($encryptedWithIv); + } + + /** + * Decrypt + * @param string $encryptedData + * @return bool|string + */ + public function decrypt(string $encryptedData): bool|string + { + $encodedData = base64_decode($encryptedData); + + $ivSize = openssl_cipher_iv_length('aes-256-cbc'); + $iv = mb_substr($encodedData, 0, $ivSize, '8bit'); + $encryptedData = mb_substr($encodedData, $ivSize, null, '8bit'); + + return openssl_decrypt($encryptedData, 'aes-256-cbc', $this->getKey(), OPENSSL_RAW_DATA, $iv); + } +} diff --git a/app/Utils/Transfer/HisSoap/ClientFactory.php b/app/Utils/Transfer/HisSoap/ClientFactory.php new file mode 100644 index 0000000..0db0311 --- /dev/null +++ b/app/Utils/Transfer/HisSoap/ClientFactory.php @@ -0,0 +1,24 @@ + new ClientTransfer($name), + true => new ClientMockTransfer($name), + }; + } +} diff --git a/app/Utils/Transfer/HisSoap/ClientMockTransfer.php b/app/Utils/Transfer/HisSoap/ClientMockTransfer.php new file mode 100644 index 0000000..fd9bb60 --- /dev/null +++ b/app/Utils/Transfer/HisSoap/ClientMockTransfer.php @@ -0,0 +1,285 @@ + $this->mockRegisterCard($request_data), + 'GetCardInfo' => $this->mockGetPatientInfo($request_data), + 'GetDepType' => $this->mockGetDepLists($request_data), + 'GetDoctList' => $this->mockGetDoctorLists($request_data), + 'GetGHMXList' => $this->mockGetRegisterRecordLists($request_data), + 'GHCancelCheck' => $this->mockCheckRefundRegisterStatus($request_data), + 'GHCancelConfirm' => $this->mockRefundRegister($request_data), + 'ListVisitRec' => $this->mockGetPendingLists($request_data), + 'ListRecipe' => $this->mockGetPendingDetails($request_data), + 'OutpatientExpenseRecord' => $this->mockGetPaidLists($request_data), + 'OutpatientDetailRecord' => $this->mockGetPaidDetails($request_data), + 'CreateOutpatientinvoiceEBill' => $this->mockCreateElectronInvoice($request_data), + 'SendOutpatientinvoiceEBill' => $this->mockSendElectronInvoiceToHis($request_data), + 'GetDictionary' => $this->mockGetDictionaryLists($request_data), + 'GetChargeList' => $this->mockGetChargeList($request_data), + default => throw new GeneralException("Method '{$method_name}' not found"), + }; + } + + /** + * 响应格式化 + * @param mixed $data + * @return mixed + * @throws Exception + */ + public function responseFormat(mixed $data): mixed + { + try { + // 此处为xml格式 + $obj = simplexml_load_string((string)$data, 'SimpleXMLElement', LIBXML_NOCDATA); + return json_decode((string)json_encode($obj), true); + + } catch (Exception $e) { + throw new Exception($e->getMessage()); + } + } + + /** + * 获取返回值 + * @param bool $is_format + * @return mixed + * @throws Exception + */ + public function getResult(bool $is_format = true): mixed + { + return $this->responseFormat($this->transfer_response); + } + + /** + * 返回值字段 + * @return string + */ + public function transferResponseStr(): string + { + return ''; + } + + /** + * mockRegisterCard + * @param array $params + * @return self + */ + private function mockRegisterCard(array $params): self + { + $this->transfer_response = '0建卡成功'; + + return $this; + } + + /** + * mockGetPatientInfo + * @param array $params + * @return self + */ + private function mockGetPatientInfo(array $params): self + { + $this->transfer_response = '01104468452323193712153735唐超积11937-12-150123299811204468'; + + return $this; + } + + /** + * mockGetDepLists + * @param array $params + * @return self + */ + private function mockGetDepLists(array $params): self + { + $this->transfer_response = '0Success1234567890内科50.00001531内科专注于诊治呼吸、消化、心血管等系统疾病。5201010152522345678901外科60.00002842外科提供专业的手术治疗服务,包括创伤、整形及器官移植。6301512203033456789012儿科40.00003621儿科为0-18岁儿童提供专业的诊疗与健康管理服务。42588101844567890123皮肤科30.00004411皮肤科诊治常见皮肤病、性病及皮肤美容问题。315765111'; + + return $this; + } + + /** + * mockGetDoctorLists + * @param array $params + * @return self + */ + private function mockGetDoctorLists(array $params): self + { + $this->transfer_response = '0Success1000120001张三内科一诊室主任医师110001300012024-12-271上午班08:0012:0050.00001201510001300022024-12-282下午班14:0018:0055.00002181210001300032024-12-293夜班20:0000:0060.00003108XM0011234567890挂号诊查费50105001000220002李四外科二诊室副主任医师010002300042024-12-271上午班08:0012:0060.00004252010002300052024-12-282下午班14:0018:0065.000052018XM0020987654321挂号诊查费100151500 +'; + + return $this; + } + + /** + * mockGetRegisterRecordLists + * @param array $params + * @return self + */ + private function mockGetRegisterRecordLists(array $params): self + { + $this->transfer_response = '0成功3405227当天挂号01436002400029682021-07-171103903杨尧2下午600005001000门诊一楼0线上预约1716急诊科3405228预约挂号1586002400029692021-07-181103904李梅1上午600005001001门诊二楼1现场挂号2520内科3405229当天挂号0236002400029702021-07-191103905张强3夜间600005001002急诊大厅2电话预约3018儿科3405230预约挂号1786002400029712021-07-201103906王丽1上午600005001003门诊三楼0线上预约4522眼科'; + + return $this; + } + + /** + * mockCheckRefundRegisterStatus + * @param array $params + * @return self + */ + private function mockCheckRefundRegisterStatus(array $params): self + { + $this->transfer_response = '0可退号'; + + return $this; + } + + /** + * mockRefundRegister + * @param array $params + * @return self + */ + private function mockRefundRegister(array $params): self + { + $this->transfer_response = '0退号成功'; + + return $this; + } + + private function mockGetPendingLists(array $params) + { + return [ + 'status' => 'success', + 'message' => 'Pending list retrieved successfully.', + 'data' => [ + [ + 'visit_no' => '12345', + 'patient_id' => $params['PATIENTID'], + 'amount' => '100.00' + ] + ] + ]; + } + + private function mockGetPendingDetails(array $params) + { + return [ + 'status' => 'success', + 'message' => 'Pending details retrieved successfully.', + 'data' => [ + 'cf_ids' => $params['CFID'], + 'jz_xh' => $params['JZXH'], + 'details' => 'Detailed description of the treatment.' + ] + ]; + } + + private function mockGetPaidLists(array $params) + { + return [ + 'status' => 'success', + 'message' => 'Paid list retrieved successfully.', + 'data' => [ + [ + 'receipt_id' => 'R12345', + 'amount' => '200.00', + 'payment_date' => '2024-01-01' + ] + ] + ]; + } + + private function mockGetPaidDetails(array $params) + { + return [ + 'status' => 'success', + 'message' => 'Paid details retrieved successfully.', + 'data' => [ + 'receipt_id' => $params['Rcptid'], + 'amount' => '200.00', + 'details' => 'Detailed description of the paid service.' + ] + ]; + } + + private function mockCreateElectronInvoice(array $params) + { + return [ + 'status' => 'success', + 'message' => 'Electron invoice created successfully.', + 'data' => $params + ]; + } + + private function mockSendElectronInvoiceToHis(array $params) + { + return [ + 'status' => 'success', + 'message' => 'Electron invoice sent successfully.', + 'data' => $params + ]; + } + + /** + * mockGetDictionaryLists + * @param array $params + * @return self + */ + private function mockGetDictionaryLists(array $params): self + { + $this->transfer_response = '01手术费2治疗费3中药费4西药费5检查费6诊查费7护理费'; + + return $this; + } + + /** + * mockGetChargeList + * @param array $params + * @return self + */ + private function mockGetChargeList(array $params): self + { + $this->transfer_response = '0西药费苯巴比妥片30MG上海信谊药厂0.097西药费阿莫西林胶囊500MG华北制药厂0.12用于治疗细菌感染西药费复方氯噻吨片0.25G长春药业0.15缓解高血压症状西药费洛伐他汀片20MG中科院制药0.25降低血脂西药费维生素C片500MG美国善格0.05增强免疫力西药费布洛芬片200MG华药集团0.1缓解轻度疼痛西药费氯氮平片25MG南京医药0.3用于治疗精神分裂症西药费头孢克肟胶囊500MG石药集团0.18抗菌药物西药费硝苯地平片10MG国药集团0.2用于治疗高血压西药费美托洛尔片25MG拜耳制药0.22用于治疗心脏病西药费甲硝唑片250MG南京同仁堂0.12用于治疗感染西药费阿托伐他汀片10MG默沙东0.3调节血脂西药费硫酸氢氯噻吨片12.5MG齐鲁制药0.15用于治疗水肿西药费兰索拉唑胶囊30MG百时美施贵宝0.18治疗胃酸过多'; + + return $this; + } +} diff --git a/app/Utils/Transfer/HisSoap/ClientTransfer.php b/app/Utils/Transfer/HisSoap/ClientTransfer.php new file mode 100644 index 0000000..dbca54a --- /dev/null +++ b/app/Utils/Transfer/HisSoap/ClientTransfer.php @@ -0,0 +1,78 @@ + config('hisservice.his_soap.url'), + SoapClientInterface::WSDL_LOCATION => config('hisservice.his_soap.location'), + SoapClientInterface::WSDL_CONNECTION_TIMEOUT => 180, + SoapClientInterface::WSDL_TRACE => 1, + SoapClientInterface::WSDL_EXCEPTIONS => true, + SoapClientInterface::WSDL_STREAM_CONTEXT => stream_context_create([ + 'ssl' => [ + // 'cafile' => base_path('cert/cacert-2024-03-11.pem'), + 'verify_peer' => false, + 'verify_peer_name' => false + ], + ]) + ]; + } + + /** + * 响应格式化 + * @param mixed $data + * @return mixed + * @throws Exception + */ + public function responseFormat(mixed $data): mixed + { + try { + // 此处为xml格式 + $obj = simplexml_load_string((string)$data, 'SimpleXMLElement', LIBXML_NOCDATA); + return json_decode((string)json_encode($obj), true); + + } catch (Exception $e) { + throw new Exception($e->getMessage()); + } + } + + /** + * 返回值字段 + * @return string + */ + public function transferResponseStr(): string + { + return $this->transfer_name. 'Result'; + } +} diff --git a/app/Utils/Transfer/TransferAbstract.php b/app/Utils/Transfer/TransferAbstract.php new file mode 100644 index 0000000..1b03e7d --- /dev/null +++ b/app/Utils/Transfer/TransferAbstract.php @@ -0,0 +1,258 @@ +his_config = $config; + $this->his_config['his_name'] = $his_name; + + $this->service_type_namespace = $this->his_config['service_type_namespace']; + $this->struct_type_namespace = $this->his_config['struct_type_namespace']; + + $this->initialize(); + } + + /** + * 初始化 + */ + public function initialize(){} + + /** + * 获取配置 + * @param string $key + * @return mixed|null + */ + public function getHisConfigByKey(string $key): mixed + { + if (isset($this->his_config[$key])) { + return $this->his_config[$key]; + } + + return false; + } + + /** + * 设置客户端选项 + * @return array + */ + abstract public function clientOption(): array; + + /** + * 设置返回值 + * @return string + */ + abstract public function transferResponseStr(): string; + + /** + * 需要调用类 + * @param string $class_name + * @return $this + * @throws InvalidArgumentException + */ + public function transferClass(string $class_name): self + { + // 实例化具体操作类 + $class_name = $this->service_type_namespace . $class_name; + + if (!class_exists($class_name)) { + throw new InvalidArgumentException($class_name. ' CLASS NOT FOUND.'); + } + + $this->client = new $class_name($this->clientOption()); + + return $this; + } + + /** + * 需要调用的方法 + * @param string $method_name + * @param array $request_data + * @return $this + * @throws InvalidArgumentException + * @throws Exception + */ + public function transferMethod(string $method_name, array $request_data = []): self + { + // 需要调用的方法的对象名 + $method_name_string = $this->struct_type_namespace . $method_name; + + // 记录调用的接口名称和参数 + $this->transfer_name = $method_name; + $this->transfer_parameter = $request_data; + + if (!class_exists($method_name_string)) { + throw new InvalidArgumentException($method_name_string. ' CLASS NOT FOUND.'); + } + + try { + $this->request_time['start_time'] = microtime(true); + $this->transfer_response = $this->client->{$method_name}(new $method_name_string(...$request_data)); + $this->request_time['end_time'] = microtime(true); + } catch (Exception $e) { + !isset($this->request_time['end_time']) && $this->request_time['end_time'] = microtime(true); + $this->transfer_response = "{$e->getFile()}:{$e->getLine()}:{$e->getMessage()}"; + $this->recordLog(); + throw new InvalidArgumentException($e->getMessage()); + } + + // 获取soap错误 + if ($this->transfer_response === false) { + $this->recordLog(); + $soap_fault = $this->client->getLastError(); + if (!empty($soap_fault)) { + $soap_fault = reset($soap_fault); + throw new Exception($soap_fault->getMessage()); + } + + throw new Exception('请求接口失败,请稍后再试'); + } + + $this->recordLog(); + return $this; + + } + + /** + * 获取返回值 + * @param bool $is_format + * @return mixed + * @throws Exception + */ + public function getResult(bool $is_format = true): mixed + { + $response_class_str = $this->struct_type_namespace . $this->transfer_name . 'Response'; + + if (!class_exists($response_class_str)) { + throw new Exception("Transfer Class Error: $response_class_str not found"); + } + + $res_str = $this->transferResponseStr(); + + if (!property_exists($this->transfer_response, $res_str)) { + throw new Exception("Transfer Attribute Error: transfer_response->$res_str not found"); + } + + $result_attr_str ='get'. ucfirst($res_str); + $this->transfer_response = new $response_class_str($this->transfer_response->$res_str); + $this->transfer_response = $this->transfer_response->$result_attr_str(); + + if ($is_format) { + return $this->responseFormat($this->transfer_response); + } + + return $this->transfer_response; + } + + /** + * 响应格式化 + * @param $data + */ + abstract public function responseFormat($data); + + /** + * 保存日志记录 + * @param string $his_name + * @param string $content + */ + public function recordRequestLog(string $his_name, string $content): void + { + date_default_timezone_set("Asia/Shanghai"); + + $dirname = $this->his_config['his_name']; + $path = app()->storagePath(). DIRECTORY_SEPARATOR. $dirname. DIRECTORY_SEPARATOR; + $file_path = $path. $his_name. '_log'. DIRECTORY_SEPARATOR. date('Ym'). DIRECTORY_SEPARATOR; + $file_name = date('d'). ".log"; + + !is_dir($file_path) && mkdir($file_path, 0755, true); + + $msg = "[".date('Y-m-d H:i:s')."]". PHP_EOL . $content . PHP_EOL . PHP_EOL; + + file_put_contents( $file_path. $file_name, $msg, FILE_APPEND); + } + + /** + * 魔术方法实现动态调用方法 + * @param $function + * @param $args + * @return $this + * @throws InvalidArgumentException + */ + public function __call($function, $args): self + { + IF (method_exists($this, $function)) { + throw new InvalidArgumentException(__CLASS__ . '类的"'. $function .'"方法不存在'); + } + + $this->client = call_user_func($function, ...$args); + return $this; + } + + /** + * 记录日志 + */ + public function recordLog(): void + { + // 判断“是否设置日志参数”和“当前调用的方法是否记录到日志” + if (!empty($this->transfer_name)) { + $run_time = sprintf("%.6f", ($this->request_time['end_time'] - $this->request_time['start_time'])); + + if ( + empty($this->his_config['not_log_arr']) || + !in_array($this->transfer_name, $this->his_config['not_log_arr']) + ) { + // 记录入参和结果 + $content = '[METHOD NAME] '. $this->transfer_name. PHP_EOL. + '[REQUEST PARAM] '. json_encode($this->transfer_parameter, JSON_UNESCAPED_UNICODE). PHP_EOL. + '[RESPONSE PARAM] '. json_encode($this->transfer_response, JSON_UNESCAPED_UNICODE). PHP_EOL. + '[RUN TIME] '. $run_time . "/s"; + $this->recordRequestLog($this->his_config['his_name'], $content); + } + } + } +} diff --git a/artisan b/artisan new file mode 100644 index 0000000..8e04b42 --- /dev/null +++ b/artisan @@ -0,0 +1,15 @@ +#!/usr/bin/env php +handleCommand(new ArgvInput); + +exit($status); diff --git a/bootstrap/app.php b/bootstrap/app.php new file mode 100644 index 0000000..06fb399 --- /dev/null +++ b/bootstrap/app.php @@ -0,0 +1,78 @@ +withRouting( + web: __DIR__.'/../routes/web.php', + api: __DIR__.'/../routes/api.php', + commands: __DIR__.'/../routes/console.php', + health: '/up', + apiPrefix: 'Api', + ) + ->withMiddleware(function (Middleware $middleware) { + + $middleware->alias([ + 'apiLog' => RecordApiLog::class + ]); + }) + ->withExceptions(function (Exceptions $exceptions) { + $exceptions->dontReport([ + GeneralException::class, + ]); + + $exceptions->render(function (Exception $exception, Request $request) { + // 错误消息 + $err_msg = $exception->getMessage(); + $record_msg = $err_msg. ' in '. $exception->getFile(). ':'. $exception->getLine(); + + // 校验入参错误 + if ($exception instanceof ValidationException) { + $err_arr = $exception->errors(); + $err_arr = array_shift($err_arr); + return jsonResponse(Response::HTTP_BAD_REQUEST, reset($err_arr)); + } + + // 接口请求方式不在路由设置中或不被允许时 + if($exception instanceof MethodNotAllowedHttpException) { + return jsonResponse(Response::HTTP_METHOD_NOT_ALLOWED, 'Method Not Allow.'); + } + + // 404异常扩展设置 + if ($exception instanceof NotFoundHttpException) { + return jsonResponse(Response::HTTP_NOT_FOUND, 'Not Found.'); + } + + // 权限 + if ($exception instanceof AuthenticationException) { + return jsonResponse(Response::HTTP_UNAUTHORIZED, 'Unauthenticated'); + } + + // 数据库错误 + if($exception instanceof PDOException) { + recordLog('DBError', $record_msg); + + return jsonResponse(Response::HTTP_INTERNAL_SERVER_ERROR, $err_msg); + } + + // 自定义错误 + if ($exception instanceof GeneralException) { + return jsonResponse(Response::HTTP_BAD_REQUEST, $err_msg); + } + + // 500 错误拦截 + recordLog('AppError', $record_msg); + return jsonResponse(Response::HTTP_INTERNAL_SERVER_ERROR, $record_msg); + }); + + })->create(); diff --git a/bootstrap/cache/.gitignore b/bootstrap/cache/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/bootstrap/cache/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/bootstrap/providers.php b/bootstrap/providers.php new file mode 100644 index 0000000..e429ce2 --- /dev/null +++ b/bootstrap/providers.php @@ -0,0 +1,5 @@ +=5.0.0" + }, + "require-dev": { + "doctrine/dbal": "^4.0.0", + "nesbot/carbon": "^2.71.0 || ^3.0.0", + "phpunit/phpunit": "^10.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Carbon\\Doctrine\\": "src/Carbon/Doctrine/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "KyleKatarn", + "email": "kylekatarnls@gmail.com" + } + ], + "description": "Types to use Carbon in Doctrine", + "keywords": [ + "carbon", + "date", + "datetime", + "doctrine", + "time" + ], + "support": { + "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", + "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2024-02-09T16:56:22+00:00" + }, + { + "name": "composer/ca-bundle", + "version": "1.5.4", + "source": { + "type": "git", + "url": "https://github.com/composer/ca-bundle.git", + "reference": "bc0593537a463e55cadf45fd938d23b75095b7e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/bc0593537a463e55cadf45fd938d23b75095b7e1", + "reference": "bc0593537a463e55cadf45fd938d23b75095b7e1", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "ext-pcre": "*", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8 || ^9", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "symfony/process": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\CaBundle\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", + "keywords": [ + "cabundle", + "cacert", + "certificate", + "ssl", + "tls" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/ca-bundle/issues", + "source": "https://github.com/composer/ca-bundle/tree/1.5.4" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-11-27T15:35:25+00:00" + }, + { + "name": "composer/class-map-generator", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/composer/class-map-generator.git", + "reference": "4b0a223cf5be7c9ee7e0ef1bc7db42b4a97c9915" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/class-map-generator/zipball/4b0a223cf5be7c9ee7e0ef1bc7db42b4a97c9915", + "reference": "4b0a223cf5be7c9ee7e0ef1bc7db42b4a97c9915", + "shasum": "" + }, + "require": { + "composer/pcre": "^2.1 || ^3.1", + "php": "^7.2 || ^8.0", + "symfony/finder": "^4.4 || ^5.3 || ^6 || ^7" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-deprecation-rules": "^1 || ^2", + "phpstan/phpstan-phpunit": "^1 || ^2", + "phpstan/phpstan-strict-rules": "^1.1 || ^2", + "phpunit/phpunit": "^8", + "symfony/filesystem": "^5.4 || ^6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\ClassMapGenerator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Utilities to scan PHP code and generate class maps.", + "keywords": [ + "classmap" + ], + "support": { + "issues": "https://github.com/composer/class-map-generator/issues", + "source": "https://github.com/composer/class-map-generator/tree/1.5.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-11-25T16:11:06+00:00" + }, + { + "name": "composer/composer", + "version": "2.8.4", + "source": { + "type": "git", + "url": "https://github.com/composer/composer.git", + "reference": "112e37d1dca22b3fdb81cf3524ab4994f47fdb8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/composer/zipball/112e37d1dca22b3fdb81cf3524ab4994f47fdb8c", + "reference": "112e37d1dca22b3fdb81cf3524ab4994f47fdb8c", + "shasum": "" + }, + "require": { + "composer/ca-bundle": "^1.5", + "composer/class-map-generator": "^1.4.0", + "composer/metadata-minifier": "^1.0", + "composer/pcre": "^2.2 || ^3.2", + "composer/semver": "^3.3", + "composer/spdx-licenses": "^1.5.7", + "composer/xdebug-handler": "^2.0.2 || ^3.0.3", + "justinrainbow/json-schema": "^5.3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "react/promise": "^2.11 || ^3.2", + "seld/jsonlint": "^1.4", + "seld/phar-utils": "^1.2", + "seld/signal-handler": "^2.0", + "symfony/console": "^5.4.35 || ^6.3.12 || ^7.0.3", + "symfony/filesystem": "^5.4.35 || ^6.3.12 || ^7.0.3", + "symfony/finder": "^5.4.35 || ^6.3.12 || ^7.0.3", + "symfony/polyfill-php73": "^1.24", + "symfony/polyfill-php80": "^1.24", + "symfony/polyfill-php81": "^1.24", + "symfony/process": "^5.4.35 || ^6.3.12 || ^7.0.3" + }, + "require-dev": { + "phpstan/phpstan": "^1.11.8", + "phpstan/phpstan-deprecation-rules": "^1.2.0", + "phpstan/phpstan-phpunit": "^1.4.0", + "phpstan/phpstan-strict-rules": "^1.6.0", + "phpstan/phpstan-symfony": "^1.4.0", + "symfony/phpunit-bridge": "^6.4.3 || ^7.0.1" + }, + "suggest": { + "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", + "ext-zip": "Enabling the zip extension allows you to unzip archives", + "ext-zlib": "Allow gzip compression of HTTP requests" + }, + "bin": [ + "bin/composer" + ], + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "phpstan/rules.neon" + ] + }, + "branch-alias": { + "dev-main": "2.8-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\": "src/Composer/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "https://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Composer helps you declare, manage and install dependencies of PHP projects. It ensures you have the right stack everywhere.", + "homepage": "https://getcomposer.org/", + "keywords": [ + "autoload", + "dependency", + "package" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/composer/issues", + "security": "https://github.com/composer/composer/security/policy", + "source": "https://github.com/composer/composer/tree/2.8.4" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-12-11T10:57:47+00:00" + }, + { + "name": "composer/metadata-minifier", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/composer/metadata-minifier.git", + "reference": "c549d23829536f0d0e984aaabbf02af91f443207" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/metadata-minifier/zipball/c549d23829536f0d0e984aaabbf02af91f443207", + "reference": "c549d23829536f0d0e984aaabbf02af91f443207", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "composer/composer": "^2", + "phpstan/phpstan": "^0.12.55", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\MetadataMinifier\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Small utility library that handles metadata minification and expansion.", + "keywords": [ + "composer", + "compression" + ], + "support": { + "issues": "https://github.com/composer/metadata-minifier/issues", + "source": "https://github.com/composer/metadata-minifier/tree/1.0.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2021-04-07T13:37:33+00:00" + }, + { + "name": "composer/pcre", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" + }, + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-11-12T16:29:46+00:00" + }, + { + "name": "composer/semver", + "version": "3.4.3", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.3" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-09-19T14:15:21+00:00" + }, + { + "name": "composer/spdx-licenses", + "version": "1.5.8", + "source": { + "type": "git", + "url": "https://github.com/composer/spdx-licenses.git", + "reference": "560bdcf8deb88ae5d611c80a2de8ea9d0358cc0a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/560bdcf8deb88ae5d611c80a2de8ea9d0358cc0a", + "reference": "560bdcf8deb88ae5d611c80a2de8ea9d0358cc0a", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.55", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Spdx\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "SPDX licenses list and validation library.", + "keywords": [ + "license", + "spdx", + "validator" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/spdx-licenses/issues", + "source": "https://github.com/composer/spdx-licenses/tree/1.5.8" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2023-11-20T07:44:33+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.5", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-05-06T16:37:16+00:00" + }, + { + "name": "dflydev/dot-access-data", + "version": "v3.0.3", + "source": { + "type": "git", + "url": "https://github.com/dflydev/dflydev-dot-access-data.git", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3", + "scrutinizer/ocular": "1.6.0", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Dflydev\\DotAccessData\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dragonfly Development Inc.", + "email": "info@dflydev.com", + "homepage": "http://dflydev.com" + }, + { + "name": "Beau Simensen", + "email": "beau@dflydev.com", + "homepage": "http://beausimensen.com" + }, + { + "name": "Carlos Frutos", + "email": "carlos@kiwing.it", + "homepage": "https://github.com/cfrutos" + }, + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com" + } + ], + "description": "Given a deep data structure, access data by dot notation.", + "homepage": "https://github.com/dflydev/dflydev-dot-access-data", + "keywords": [ + "access", + "data", + "dot", + "notation" + ], + "support": { + "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3" + }, + "time": "2024-07-08T12:26:09+00:00" + }, + { + "name": "doctrine/inflector", + "version": "2.0.10", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^11.0", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.3", + "phpunit/phpunit": "^8.5 || ^9.5", + "vimeo/psalm": "^4.25 || ^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", + "keywords": [ + "inflection", + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.0.10" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" + } + ], + "time": "2024-02-18T20:23:39+00:00" + }, + { + "name": "doctrine/lexer", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2024-02-05T11:56:58+00:00" + }, + { + "name": "dragonmantank/cron-expression", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/dragonmantank/cron-expression.git", + "reference": "8c784d071debd117328803d86b2097615b457500" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/8c784d071debd117328803d86b2097615b457500", + "reference": "8c784d071debd117328803d86b2097615b457500", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0", + "webmozart/assert": "^1.0" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Cron\\": "src/Cron/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Tankersley", + "email": "chris@ctankersley.com", + "homepage": "https://github.com/dragonmantank" + } + ], + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "keywords": [ + "cron", + "schedule" + ], + "support": { + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.4.0" + }, + "funding": [ + { + "url": "https://github.com/dragonmantank", + "type": "github" + } + ], + "time": "2024-10-09T13:47:03+00:00" + }, + { + "name": "egulias/email-validator", + "version": "4.0.2", + "source": { + "type": "git", + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ebaaf5be6c0286928352e054f2d5125608e5405e", + "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" + }, + "require-dev": { + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" + }, + "suggest": { + "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Egulias\\EmailValidator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eduardo Gulias Davis" + } + ], + "description": "A library for validating emails against several RFCs", + "homepage": "https://github.com/egulias/EmailValidator", + "keywords": [ + "email", + "emailvalidation", + "emailvalidator", + "validation", + "validator" + ], + "support": { + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/4.0.2" + }, + "funding": [ + { + "url": "https://github.com/egulias", + "type": "github" + } + ], + "time": "2023-10-06T06:47:41+00:00" + }, + { + "name": "fruitcake/php-cors", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/fruitcake/php-cors.git", + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/3d158f36e7875e2f040f37bc0573956240a5a38b", + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0", + "symfony/http-foundation": "^4.4|^5.4|^6|^7" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "Fruitcake\\Cors\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fruitcake", + "homepage": "https://fruitcake.nl" + }, + { + "name": "Barryvdh", + "email": "barryvdh@gmail.com" + } + ], + "description": "Cross-origin resource sharing library for the Symfony HttpFoundation", + "homepage": "https://github.com/fruitcake/php-cors", + "keywords": [ + "cors", + "laravel", + "symfony" + ], + "support": { + "issues": "https://github.com/fruitcake/php-cors/issues", + "source": "https://github.com/fruitcake/php-cors/tree/v1.3.0" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2023-10-12T05:21:21+00:00" + }, + { + "name": "graham-campbell/result-type", + "version": "v1.1.3", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945", + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.3" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\ResultType\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "An Implementation Of The Result Type", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Result Type", + "Result-Type", + "result" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2024-07-20T21:45:45+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.9.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5.3 || ^2.0.3", + "guzzlehttp/psr7": "^2.7.0", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.9.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2024-07-24T11:22:20+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2024-10-17T10:06:22+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.7.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.7.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2024-07-18T11:15:46+00:00" + }, + { + "name": "guzzlehttp/uri-template", + "version": "v1.0.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/uri-template.git", + "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/ecea8feef63bd4fef1f037ecb288386999ecc11c", + "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "symfony/polyfill-php80": "^1.24" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "uri-template/tests": "1.0.0" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\UriTemplate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + } + ], + "description": "A polyfill class for uri_template of PHP", + "keywords": [ + "guzzlehttp", + "uri-template" + ], + "support": { + "issues": "https://github.com/guzzle/uri-template/issues", + "source": "https://github.com/guzzle/uri-template/tree/v1.0.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/uri-template", + "type": "tidelift" + } + ], + "time": "2023-12-03T19:50:20+00:00" + }, + { + "name": "justinrainbow/json-schema", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/jsonrainbow/json-schema.git", + "reference": "feb2ca6dd1cebdaf1ed60a4c8de2e53ce11c4fd8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/feb2ca6dd1cebdaf1ed60a4c8de2e53ce11c4fd8", + "reference": "feb2ca6dd1cebdaf1ed60a4c8de2e53ce11c4fd8", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", + "json-schema/json-schema-test-suite": "1.2.0", + "phpunit/phpunit": "^4.8.35" + }, + "bin": [ + "bin/validate-json" + ], + "type": "library", + "autoload": { + "psr-4": { + "JsonSchema\\": "src/JsonSchema/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "support": { + "issues": "https://github.com/jsonrainbow/json-schema/issues", + "source": "https://github.com/jsonrainbow/json-schema/tree/5.3.0" + }, + "time": "2024-07-06T21:00:26+00:00" + }, + { + "name": "laravel/framework", + "version": "v11.36.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/framework.git", + "reference": "df06f5163f4550641fdf349ebc04916a61135a64" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/framework/zipball/df06f5163f4550641fdf349ebc04916a61135a64", + "reference": "df06f5163f4550641fdf349ebc04916a61135a64", + "shasum": "" + }, + "require": { + "brick/math": "^0.9.3|^0.10.2|^0.11|^0.12", + "composer-runtime-api": "^2.2", + "doctrine/inflector": "^2.0.5", + "dragonmantank/cron-expression": "^3.4", + "egulias/email-validator": "^3.2.1|^4.0", + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "ext-session": "*", + "ext-tokenizer": "*", + "fruitcake/php-cors": "^1.3", + "guzzlehttp/guzzle": "^7.8.2", + "guzzlehttp/uri-template": "^1.0", + "laravel/prompts": "^0.1.18|^0.2.0|^0.3.0", + "laravel/serializable-closure": "^1.3|^2.0", + "league/commonmark": "^2.6", + "league/flysystem": "^3.25.1", + "league/flysystem-local": "^3.25.1", + "league/uri": "^7.5.1", + "monolog/monolog": "^3.0", + "nesbot/carbon": "^2.72.2|^3.4", + "nunomaduro/termwind": "^2.0", + "php": "^8.2", + "psr/container": "^1.1.1|^2.0.1", + "psr/log": "^1.0|^2.0|^3.0", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "ramsey/uuid": "^4.7", + "symfony/console": "^7.0.3", + "symfony/error-handler": "^7.0.3", + "symfony/finder": "^7.0.3", + "symfony/http-foundation": "^7.2.0", + "symfony/http-kernel": "^7.0.3", + "symfony/mailer": "^7.0.3", + "symfony/mime": "^7.0.3", + "symfony/polyfill-php83": "^1.31", + "symfony/process": "^7.0.3", + "symfony/routing": "^7.0.3", + "symfony/uid": "^7.0.3", + "symfony/var-dumper": "^7.0.3", + "tijsverkoyen/css-to-inline-styles": "^2.2.5", + "vlucas/phpdotenv": "^5.6.1", + "voku/portable-ascii": "^2.0.2" + }, + "conflict": { + "mockery/mockery": "1.6.8", + "tightenco/collect": "<5.5.33" + }, + "provide": { + "psr/container-implementation": "1.1|2.0", + "psr/log-implementation": "1.0|2.0|3.0", + "psr/simple-cache-implementation": "1.0|2.0|3.0" + }, + "replace": { + "illuminate/auth": "self.version", + "illuminate/broadcasting": "self.version", + "illuminate/bus": "self.version", + "illuminate/cache": "self.version", + "illuminate/collections": "self.version", + "illuminate/concurrency": "self.version", + "illuminate/conditionable": "self.version", + "illuminate/config": "self.version", + "illuminate/console": "self.version", + "illuminate/container": "self.version", + "illuminate/contracts": "self.version", + "illuminate/cookie": "self.version", + "illuminate/database": "self.version", + "illuminate/encryption": "self.version", + "illuminate/events": "self.version", + "illuminate/filesystem": "self.version", + "illuminate/hashing": "self.version", + "illuminate/http": "self.version", + "illuminate/log": "self.version", + "illuminate/macroable": "self.version", + "illuminate/mail": "self.version", + "illuminate/notifications": "self.version", + "illuminate/pagination": "self.version", + "illuminate/pipeline": "self.version", + "illuminate/process": "self.version", + "illuminate/queue": "self.version", + "illuminate/redis": "self.version", + "illuminate/routing": "self.version", + "illuminate/session": "self.version", + "illuminate/support": "self.version", + "illuminate/testing": "self.version", + "illuminate/translation": "self.version", + "illuminate/validation": "self.version", + "illuminate/view": "self.version", + "spatie/once": "*" + }, + "require-dev": { + "ably/ably-php": "^1.0", + "aws/aws-sdk-php": "^3.322.9", + "ext-gmp": "*", + "fakerphp/faker": "^1.24", + "guzzlehttp/promises": "^2.0.3", + "guzzlehttp/psr7": "^2.4", + "league/flysystem-aws-s3-v3": "^3.25.1", + "league/flysystem-ftp": "^3.25.1", + "league/flysystem-path-prefixing": "^3.25.1", + "league/flysystem-read-only": "^3.25.1", + "league/flysystem-sftp-v3": "^3.25.1", + "mockery/mockery": "^1.6.10", + "orchestra/testbench-core": "^9.6", + "pda/pheanstalk": "^5.0.6", + "php-http/discovery": "^1.15", + "phpstan/phpstan": "^1.11.5", + "phpunit/phpunit": "^10.5.35|^11.3.6", + "predis/predis": "^2.3", + "resend/resend-php": "^0.10.0", + "symfony/cache": "^7.0.3", + "symfony/http-client": "^7.0.3", + "symfony/psr-http-message-bridge": "^7.0.3", + "symfony/translation": "^7.0.3" + }, + "suggest": { + "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", + "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.322.9).", + "brianium/paratest": "Required to run tests in parallel (^7.0|^8.0).", + "ext-apcu": "Required to use the APC cache driver.", + "ext-fileinfo": "Required to use the Filesystem class.", + "ext-ftp": "Required to use the Flysystem FTP driver.", + "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().", + "ext-memcached": "Required to use the memcache cache driver.", + "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.", + "ext-pdo": "Required to use all database features.", + "ext-posix": "Required to use all features of the queue worker.", + "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0|^6.0).", + "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", + "filp/whoops": "Required for friendly error pages in development (^2.14.3).", + "laravel/tinker": "Required to use the tinker console command (^2.0).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.25.1).", + "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.25.1).", + "league/flysystem-read-only": "Required to use read-only disks (^3.25.1)", + "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).", + "mockery/mockery": "Required to use mocking (^1.6).", + "pda/pheanstalk": "Required to use the beanstalk queue driver (^5.0).", + "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).", + "phpunit/phpunit": "Required to use assertions and run tests (^10.5|^11.0).", + "predis/predis": "Required to use the predis connector (^2.3).", + "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).", + "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0).", + "symfony/cache": "Required to PSR-6 cache bridge (^7.0).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^7.0).", + "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.0).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.0).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.0).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "11.x-dev" + } + }, + "autoload": { + "files": [ + "src/Illuminate/Collections/functions.php", + "src/Illuminate/Collections/helpers.php", + "src/Illuminate/Events/functions.php", + "src/Illuminate/Filesystem/functions.php", + "src/Illuminate/Foundation/helpers.php", + "src/Illuminate/Log/functions.php", + "src/Illuminate/Support/functions.php", + "src/Illuminate/Support/helpers.php" + ], + "psr-4": { + "Illuminate\\": "src/Illuminate/", + "Illuminate\\Support\\": [ + "src/Illuminate/Macroable/", + "src/Illuminate/Collections/", + "src/Illuminate/Conditionable/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Laravel Framework.", + "homepage": "https://laravel.com", + "keywords": [ + "framework", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2024-12-17T22:32:08+00:00" + }, + { + "name": "laravel/prompts", + "version": "v0.3.2", + "source": { + "type": "git", + "url": "https://github.com/laravel/prompts.git", + "reference": "0e0535747c6b8d6d10adca8b68293cf4517abb0f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/prompts/zipball/0e0535747c6b8d6d10adca8b68293cf4517abb0f", + "reference": "0e0535747c6b8d6d10adca8b68293cf4517abb0f", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.2", + "ext-mbstring": "*", + "php": "^8.1", + "symfony/console": "^6.2|^7.0" + }, + "conflict": { + "illuminate/console": ">=10.17.0 <10.25.0", + "laravel/framework": ">=10.17.0 <10.25.0" + }, + "require-dev": { + "illuminate/collections": "^10.0|^11.0", + "mockery/mockery": "^1.5", + "pestphp/pest": "^2.3|^3.4", + "phpstan/phpstan": "^1.11", + "phpstan/phpstan-mockery": "^1.1" + }, + "suggest": { + "ext-pcntl": "Required for the spinner to be animated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.3.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Laravel\\Prompts\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Add beautiful and user-friendly forms to your command-line applications.", + "support": { + "issues": "https://github.com/laravel/prompts/issues", + "source": "https://github.com/laravel/prompts/tree/v0.3.2" + }, + "time": "2024-11-12T14:59:47+00:00" + }, + { + "name": "laravel/sanctum", + "version": "v4.0.7", + "source": { + "type": "git", + "url": "https://github.com/laravel/sanctum.git", + "reference": "698064236a46df016e64a7eb059b1414e0b281df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/698064236a46df016e64a7eb059b1414e0b281df", + "reference": "698064236a46df016e64a7eb059b1414e0b281df", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/console": "^11.0", + "illuminate/contracts": "^11.0", + "illuminate/database": "^11.0", + "illuminate/support": "^11.0", + "php": "^8.2", + "symfony/console": "^7.0" + }, + "require-dev": { + "mockery/mockery": "^1.6", + "orchestra/testbench": "^9.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Sanctum\\SanctumServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sanctum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.", + "keywords": [ + "auth", + "laravel", + "sanctum" + ], + "support": { + "issues": "https://github.com/laravel/sanctum/issues", + "source": "https://github.com/laravel/sanctum" + }, + "time": "2024-12-11T16:40:21+00:00" + }, + { + "name": "laravel/serializable-closure", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/serializable-closure.git", + "reference": "613b2d4998f85564d40497e05e89cb6d9bd1cbe8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/613b2d4998f85564d40497e05e89cb6d9bd1cbe8", + "reference": "613b2d4998f85564d40497e05e89cb6d9bd1cbe8", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "illuminate/support": "^10.0|^11.0", + "nesbot/carbon": "^2.67|^3.0", + "pestphp/pest": "^2.36", + "phpstan/phpstan": "^2.0", + "symfony/var-dumper": "^6.2.0|^7.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\SerializableClosure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "nuno@laravel.com" + } + ], + "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", + "keywords": [ + "closure", + "laravel", + "serializable" + ], + "support": { + "issues": "https://github.com/laravel/serializable-closure/issues", + "source": "https://github.com/laravel/serializable-closure" + }, + "time": "2024-12-16T15:26:28+00:00" + }, + { + "name": "laravel/tinker", + "version": "v2.10.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/tinker.git", + "reference": "ba4d51eb56de7711b3a37d63aa0643e99a339ae5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/tinker/zipball/ba4d51eb56de7711b3a37d63aa0643e99a339ae5", + "reference": "ba4d51eb56de7711b3a37d63aa0643e99a339ae5", + "shasum": "" + }, + "require": { + "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "php": "^7.2.5|^8.0", + "psy/psysh": "^0.11.1|^0.12.0", + "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0" + }, + "require-dev": { + "mockery/mockery": "~1.3.3|^1.4.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.5.8|^9.3.3" + }, + "suggest": { + "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0|^11.0)." + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Tinker\\TinkerServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Tinker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Powerful REPL for the Laravel framework.", + "keywords": [ + "REPL", + "Tinker", + "laravel", + "psysh" + ], + "support": { + "issues": "https://github.com/laravel/tinker/issues", + "source": "https://github.com/laravel/tinker/tree/v2.10.0" + }, + "time": "2024-09-23T13:32:56+00:00" + }, + { + "name": "league/commonmark", + "version": "2.6.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/commonmark.git", + "reference": "d150f911e0079e90ae3c106734c93137c184f932" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/d150f911e0079e90ae3c106734c93137c184f932", + "reference": "d150f911e0079e90ae3c106734c93137c184f932", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "league/config": "^1.1.1", + "php": "^7.4 || ^8.0", + "psr/event-dispatcher": "^1.0", + "symfony/deprecation-contracts": "^2.1 || ^3.0", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "cebe/markdown": "^1.0", + "commonmark/cmark": "0.31.1", + "commonmark/commonmark.js": "0.31.1", + "composer/package-versions-deprecated": "^1.8", + "embed/embed": "^4.4", + "erusev/parsedown": "^1.0", + "ext-json": "*", + "github/gfm": "0.29.0", + "michelf/php-markdown": "^1.4 || ^2.0", + "nyholm/psr7": "^1.5", + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0", + "scrutinizer/ocular": "^1.8.1", + "symfony/finder": "^5.3 | ^6.0 | ^7.0", + "symfony/process": "^5.4 | ^6.0 | ^7.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0", + "unleashedtech/php-coding-standard": "^3.1.1", + "vimeo/psalm": "^4.24.0 || ^5.0.0" + }, + "suggest": { + "symfony/yaml": "v2.3+ required if using the Front Matter extension" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "League\\CommonMark\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)", + "homepage": "https://commonmark.thephpleague.com", + "keywords": [ + "commonmark", + "flavored", + "gfm", + "github", + "github-flavored", + "markdown", + "md", + "parser" + ], + "support": { + "docs": "https://commonmark.thephpleague.com/", + "forum": "https://github.com/thephpleague/commonmark/discussions", + "issues": "https://github.com/thephpleague/commonmark/issues", + "rss": "https://github.com/thephpleague/commonmark/releases.atom", + "source": "https://github.com/thephpleague/commonmark" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/commonmark", + "type": "tidelift" + } + ], + "time": "2024-12-07T15:34:16+00:00" + }, + { + "name": "league/config", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/config.git", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/config/zipball/754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^3.0.1", + "nette/schema": "^1.2", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.5", + "scrutinizer/ocular": "^1.8.1", + "unleashedtech/php-coding-standard": "^3.1", + "vimeo/psalm": "^4.7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Config\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Define configuration arrays with strict schemas and access values with dot notation", + "homepage": "https://config.thephpleague.com", + "keywords": [ + "array", + "config", + "configuration", + "dot", + "dot-access", + "nested", + "schema" + ], + "support": { + "docs": "https://config.thephpleague.com/", + "issues": "https://github.com/thephpleague/config/issues", + "rss": "https://github.com/thephpleague/config/releases.atom", + "source": "https://github.com/thephpleague/config" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + } + ], + "time": "2022-12-11T20:36:23+00:00" + }, + { + "name": "league/flysystem", + "version": "3.29.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/edc1bb7c86fab0776c3287dbd19b5fa278347319", + "reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319", + "shasum": "" + }, + "require": { + "league/flysystem-local": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "conflict": { + "async-aws/core": "<1.19.0", + "async-aws/s3": "<1.14.0", + "aws/aws-sdk-php": "3.209.31 || 3.210.0", + "guzzlehttp/guzzle": "<7.0", + "guzzlehttp/ringphp": "<1.1.1", + "phpseclib/phpseclib": "3.0.15", + "symfony/http-client": "<5.2" + }, + "require-dev": { + "async-aws/s3": "^1.5 || ^2.0", + "async-aws/simple-s3": "^1.1 || ^2.0", + "aws/aws-sdk-php": "^3.295.10", + "composer/semver": "^3.0", + "ext-fileinfo": "*", + "ext-ftp": "*", + "ext-mongodb": "^1.3", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.5", + "google/cloud-storage": "^1.23", + "guzzlehttp/psr7": "^2.6", + "microsoft/azure-storage-blob": "^1.1", + "mongodb/mongodb": "^1.2", + "phpseclib/phpseclib": "^3.0.36", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.11|^10.0", + "sabre/dav": "^4.6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "File storage abstraction for PHP", + "keywords": [ + "WebDAV", + "aws", + "cloud", + "file", + "files", + "filesystem", + "filesystems", + "ftp", + "s3", + "sftp", + "storage" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem/issues", + "source": "https://github.com/thephpleague/flysystem/tree/3.29.1" + }, + "time": "2024-10-08T08:58:34+00:00" + }, + { + "name": "league/flysystem-local", + "version": "3.29.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-local.git", + "reference": "e0e8d52ce4b2ed154148453d321e97c8e931bd27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/e0e8d52ce4b2ed154148453d321e97c8e931bd27", + "reference": "e0e8d52ce4b2ed154148453d321e97c8e931bd27", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "league/flysystem": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\Local\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Local filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "file", + "files", + "filesystem", + "local" + ], + "support": { + "source": "https://github.com/thephpleague/flysystem-local/tree/3.29.0" + }, + "time": "2024-08-09T21:24:39+00:00" + }, + { + "name": "league/mime-type-detection", + "version": "1.16.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/mime-type-detection.git", + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/2d6702ff215bf922936ccc1ad31007edc76451b9", + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "phpstan/phpstan": "^0.12.68", + "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\MimeTypeDetection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Mime-type detection for Flysystem", + "support": { + "issues": "https://github.com/thephpleague/mime-type-detection/issues", + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.16.0" + }, + "funding": [ + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "time": "2024-09-21T08:32:55+00:00" + }, + { + "name": "league/uri", + "version": "7.5.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri.git", + "reference": "81fb5145d2644324614cc532b28efd0215bda430" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/81fb5145d2644324614cc532b28efd0215bda430", + "reference": "81fb5145d2644324614cc532b28efd0215bda430", + "shasum": "" + }, + "require": { + "league/uri-interfaces": "^7.5", + "php": "^8.1" + }, + "conflict": { + "league/uri-schemes": "^1.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", + "league/uri-components": "Needed to easily manipulate URI objects components", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "URI manipulation library", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "middleware", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "uri-template", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri/tree/7.5.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2024-12-08T08:40:02+00:00" + }, + { + "name": "league/uri-interfaces", + "version": "7.5.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^8.1", + "psr/http-factory": "^1", + "psr/http-message": "^1.1 || ^2.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "Common interfaces and classes for URI representation and interaction", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.5.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2024-12-08T08:18:47+00:00" + }, + { + "name": "monolog/monolog", + "version": "3.8.1", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "aef6ee73a77a66e404dd6540934a9ef1b3c855b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/aef6ee73a77a66e404dd6540934a9ef1b3c855b4", + "reference": "aef6ee73a77a66e404dd6540934a9ef1b3c855b4", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2.0", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "php-console/php-console": "^3.1.8", + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^10.5.17 || ^11.0.7", + "predis/predis": "^1.1 || ^2", + "rollbar/rollbar": "^4.0", + "ruflin/elastica": "^7 || ^8", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/3.8.1" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2024-12-05T17:15:07+00:00" + }, + { + "name": "nesbot/carbon", + "version": "3.8.3", + "source": { + "type": "git", + "url": "https://github.com/briannesbitt/Carbon.git", + "reference": "f01cfa96468f4c38325f507ab81a4f1d2cd93cfe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/f01cfa96468f4c38325f507ab81a4f1d2cd93cfe", + "reference": "f01cfa96468f4c38325f507ab81a4f1d2cd93cfe", + "shasum": "" + }, + "require": { + "carbonphp/carbon-doctrine-types": "<100.0", + "ext-json": "*", + "php": "^8.1", + "psr/clock": "^1.0", + "symfony/clock": "^6.3 || ^7.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/translation": "^4.4.18 || ^5.2.1|| ^6.0 || ^7.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "doctrine/dbal": "^3.6.3 || ^4.0", + "doctrine/orm": "^2.15.2 || ^3.0", + "friendsofphp/php-cs-fixer": "^3.57.2", + "kylekatarnls/multi-tester": "^2.5.3", + "ondrejmirtes/better-reflection": "^6.25.0.4", + "phpmd/phpmd": "^2.15.0", + "phpstan/extension-installer": "^1.3.1", + "phpstan/phpstan": "^1.11.2", + "phpunit/phpunit": "^10.5.20", + "squizlabs/php_codesniffer": "^3.9.0" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev", + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Carbon\\": "src/Carbon/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "https://markido.com" + }, + { + "name": "kylekatarnls", + "homepage": "https://github.com/kylekatarnls" + } + ], + "description": "An API extension for DateTime that supports 281 different languages.", + "homepage": "https://carbon.nesbot.com", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "docs": "https://carbon.nesbot.com/docs", + "issues": "https://github.com/briannesbitt/Carbon/issues", + "source": "https://github.com/briannesbitt/Carbon" + }, + "funding": [ + { + "url": "https://github.com/sponsors/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon#sponsor", + "type": "opencollective" + }, + { + "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme", + "type": "tidelift" + } + ], + "time": "2024-12-21T18:03:19+00:00" + }, + { + "name": "nette/schema", + "version": "v1.3.2", + "source": { + "type": "git", + "url": "https://github.com/nette/schema.git", + "reference": "da801d52f0354f70a638673c4a0f04e16529431d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/schema/zipball/da801d52f0354f70a638673c4a0f04e16529431d", + "reference": "da801d52f0354f70a638673c4a0f04e16529431d", + "shasum": "" + }, + "require": { + "nette/utils": "^4.0", + "php": "8.1 - 8.4" + }, + "require-dev": { + "nette/tester": "^2.5.2", + "phpstan/phpstan-nette": "^1.0", + "tracy/tracy": "^2.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "📐 Nette Schema: validating data structures against a given Schema.", + "homepage": "https://nette.org", + "keywords": [ + "config", + "nette" + ], + "support": { + "issues": "https://github.com/nette/schema/issues", + "source": "https://github.com/nette/schema/tree/v1.3.2" + }, + "time": "2024-10-06T23:10:23+00:00" + }, + { + "name": "nette/utils", + "version": "v4.0.5", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/736c567e257dbe0fcf6ce81b4d6dbe05c6899f96", + "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96", + "shasum": "" + }, + "require": { + "php": "8.0 - 8.4" + }, + "conflict": { + "nette/finder": "<3", + "nette/schema": "<1.2.2" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "dev-master", + "nette/tester": "^2.5", + "phpstan/phpstan": "^1.0", + "tracy/tracy": "^2.9" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", + "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "support": { + "issues": "https://github.com/nette/utils/issues", + "source": "https://github.com/nette/utils/tree/v4.0.5" + }, + "time": "2024-08-07T15:39:19+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.3.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" + }, + "time": "2024-10-08T18:51:32+00:00" + }, + { + "name": "nunomaduro/termwind", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/termwind.git", + "reference": "52915afe6a1044e8b9cee1bcff836fb63acf9cda" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/52915afe6a1044e8b9cee1bcff836fb63acf9cda", + "reference": "52915afe6a1044e8b9cee1bcff836fb63acf9cda", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.2", + "symfony/console": "^7.1.8" + }, + "require-dev": { + "illuminate/console": "^11.33.2", + "laravel/pint": "^1.18.2", + "mockery/mockery": "^1.6.12", + "pestphp/pest": "^2.36.0", + "phpstan/phpstan": "^1.12.11", + "phpstan/phpstan-strict-rules": "^1.6.1", + "symfony/var-dumper": "^7.1.8", + "thecodingmachine/phpstan-strict-rules": "^1.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Termwind\\Laravel\\TermwindServiceProvider" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "files": [ + "src/Functions.php" + ], + "psr-4": { + "Termwind\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Its like Tailwind CSS, but for the console.", + "keywords": [ + "cli", + "console", + "css", + "package", + "php", + "style" + ], + "support": { + "issues": "https://github.com/nunomaduro/termwind/issues", + "source": "https://github.com/nunomaduro/termwind/tree/v2.3.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://github.com/xiCO2k", + "type": "github" + } + ], + "time": "2024-11-21T10:39:51+00:00" + }, + { + "name": "nyholm/psr7", + "version": "1.8.2", + "source": { + "type": "git", + "url": "https://github.com/Nyholm/psr7.git", + "reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Nyholm/psr7/zipball/a71f2b11690f4b24d099d6b16690a90ae14fc6f3", + "reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0" + }, + "provide": { + "php-http/message-factory-implementation": "1.0", + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "http-interop/http-factory-tests": "^0.9", + "php-http/message-factory": "^1.0", + "php-http/psr7-integration-tests": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.4", + "symfony/error-handler": "^4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "Nyholm\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com" + }, + { + "name": "Martijn van der Ven", + "email": "martijn@vanderven.se" + } + ], + "description": "A fast PHP7 implementation of PSR-7", + "homepage": "https://tnyholm.se", + "keywords": [ + "psr-17", + "psr-7" + ], + "support": { + "issues": "https://github.com/Nyholm/psr7/issues", + "source": "https://github.com/Nyholm/psr7/tree/1.8.2" + }, + "funding": [ + { + "url": "https://github.com/Zegnat", + "type": "github" + }, + { + "url": "https://github.com/nyholm", + "type": "github" + } + ], + "time": "2024-09-09T07:06:30+00:00" + }, + { + "name": "nyholm/psr7-server", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/Nyholm/psr7-server.git", + "reference": "4335801d851f554ca43fa6e7d2602141538854dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Nyholm/psr7-server/zipball/4335801d851f554ca43fa6e7d2602141538854dc", + "reference": "4335801d851f554ca43fa6e7d2602141538854dc", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "require-dev": { + "nyholm/nsa": "^1.1", + "nyholm/psr7": "^1.3", + "phpunit/phpunit": "^7.0 || ^8.5 || ^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Nyholm\\Psr7Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com" + }, + { + "name": "Martijn van der Ven", + "email": "martijn@vanderven.se" + } + ], + "description": "Helper classes to handle PSR-7 server requests", + "homepage": "http://tnyholm.se", + "keywords": [ + "psr-17", + "psr-7" + ], + "support": { + "issues": "https://github.com/Nyholm/psr7-server/issues", + "source": "https://github.com/Nyholm/psr7-server/tree/1.1.0" + }, + "funding": [ + { + "url": "https://github.com/Zegnat", + "type": "github" + }, + { + "url": "https://github.com/nyholm", + "type": "github" + } + ], + "time": "2023-11-08T09:30:43+00:00" + }, + { + "name": "overtrue/laravel-wechat", + "version": "7.3.0", + "source": { + "type": "git", + "url": "https://github.com/overtrue/laravel-wechat.git", + "reference": "b3e89dddc45da4eb3d5322a10dee9dc65e619579" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/overtrue/laravel-wechat/zipball/b3e89dddc45da4eb3d5322a10dee9dc65e619579", + "reference": "b3e89dddc45da4eb3d5322a10dee9dc65e619579", + "shasum": "" + }, + "require": { + "illuminate/container": "^9.0|^10.0|^11.0", + "w7corp/easywechat": "^6.0.0" + }, + "require-dev": { + "brainmaestro/composer-git-hooks": "dev-master", + "jetbrains/phpstorm-attributes": "^1.0", + "laravel/framework": "^10.0", + "laravel/pint": "^1.5" + }, + "type": "library", + "extra": { + "hooks": { + "pre-push": [ + "composer check-style" + ], + "pre-commit": [ + "composer check-style" + ] + }, + "laravel": { + "providers": [ + "Overtrue\\LaravelWeChat\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Overtrue\\LaravelWeChat\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "overtrue", + "email": "anzhengchao@gmail.com" + } + ], + "description": "微信 SDK for Laravel", + "keywords": [ + "laravel", + "sdk", + "wechat", + "weixin" + ], + "support": { + "issues": "https://github.com/overtrue/laravel-wechat/issues", + "source": "https://github.com/overtrue/laravel-wechat/tree/7.3.0" + }, + "funding": [ + { + "url": "https://github.com/overtrue", + "type": "github" + } + ], + "time": "2024-03-13T02:06:36+00:00" + }, + { + "name": "overtrue/socialite", + "version": "4.11.2", + "source": { + "type": "git", + "url": "https://github.com/overtrue/socialite.git", + "reference": "83dd537a88b30cd9204ee2c46a5b2e181bc1fa66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/overtrue/socialite/zipball/83dd537a88b30cd9204ee2c46a5b2e181bc1fa66", + "reference": "83dd537a88b30cd9204ee2c46a5b2e181bc1fa66", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-openssl": "*", + "guzzlehttp/guzzle": "^7.0", + "php": ">=8.0.2" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "^1.0", + "laravel/pint": "^1.2", + "mockery/mockery": "^1.3", + "phpstan/phpstan": "^1.7", + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "autoload": { + "files": [ + "src/Contracts/FactoryInterface.php", + "src/Contracts/UserInterface.php", + "src/Contracts/ProviderInterface.php" + ], + "psr-4": { + "Overtrue\\Socialite\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "overtrue", + "email": "anzhengchao@gmail.com" + } + ], + "description": "A collection of OAuth 2 packages.", + "keywords": [ + "Feishu", + "login", + "oauth", + "qcloud", + "qq", + "social", + "wechat", + "weibo" + ], + "support": { + "issues": "https://github.com/overtrue/socialite/issues", + "source": "https://github.com/overtrue/socialite/tree/4.11.2" + }, + "funding": [ + { + "url": "https://github.com/overtrue", + "type": "github" + } + ], + "time": "2024-10-08T16:23:14+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.9.3", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54", + "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.9.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2024-07-20T21:41:07+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "1.12.13", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "9b469068840cfa031e1deaf2fa1886d00e20680f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9b469068840cfa031e1deaf2fa1886d00e20680f", + "reference": "9b469068840cfa031e1deaf2fa1886d00e20680f", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2024-12-17T17:00:20+00:00" + }, + { + "name": "psr/cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, + { + "name": "psy/psysh", + "version": "v0.12.7", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/psysh.git", + "reference": "d73fa3c74918ef4522bb8a3bf9cab39161c4b57c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/d73fa3c74918ef4522bb8a3bf9cab39161c4b57c", + "reference": "d73fa3c74918ef4522bb8a3bf9cab39161c4b57c", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "nikic/php-parser": "^5.0 || ^4.0", + "php": "^8.0 || ^7.4", + "symfony/console": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", + "symfony/var-dumper": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" + }, + "conflict": { + "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.2" + }, + "suggest": { + "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", + "ext-pdo-sqlite": "The doc command requires SQLite to work.", + "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well." + }, + "bin": [ + "bin/psysh" + ], + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": false, + "forward-command": false + }, + "branch-alias": { + "dev-main": "0.12.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Psy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info", + "homepage": "http://justinhileman.com" + } + ], + "description": "An interactive shell for modern PHP.", + "homepage": "http://psysh.org", + "keywords": [ + "REPL", + "console", + "interactive", + "shell" + ], + "support": { + "issues": "https://github.com/bobthecow/psysh/issues", + "source": "https://github.com/bobthecow/psysh/tree/v0.12.7" + }, + "time": "2024-12-10T01:58:33+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "ramsey/collection", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/ramsey/collection.git", + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "captainhook/plugin-composer": "^5.3", + "ergebnis/composer-normalize": "^2.28.3", + "fakerphp/faker": "^1.21", + "hamcrest/hamcrest-php": "^2.0", + "jangregor/phpstan-prophecy": "^1.0", + "mockery/mockery": "^1.5", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpcsstandards/phpcsutils": "^1.0.0-rc1", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.18.4", + "ramsey/coding-standard": "^2.0.3", + "ramsey/conventional-commits": "^1.3", + "vimeo/psalm": "^5.4" + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" + } + }, + "autoload": { + "psr-4": { + "Ramsey\\Collection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A PHP library for representing and manipulating collections.", + "keywords": [ + "array", + "collection", + "hash", + "map", + "queue", + "set" + ], + "support": { + "issues": "https://github.com/ramsey/collection/issues", + "source": "https://github.com/ramsey/collection/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", + "type": "tidelift" + } + ], + "time": "2022-12-31T21:50:55+00:00" + }, + { + "name": "ramsey/uuid", + "version": "4.7.6", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "91039bc1faa45ba123c4328958e620d382ec7088" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088", + "reference": "91039bc1faa45ba123c4328958e620d382ec7088", + "shasum": "" + }, + "require": { + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12", + "ext-json": "*", + "php": "^8.0", + "ramsey/collection": "^1.2 || ^2.0" + }, + "replace": { + "rhumsaa/uuid": "self.version" + }, + "require-dev": { + "captainhook/captainhook": "^5.10", + "captainhook/plugin-composer": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "doctrine/annotations": "^1.8", + "ergebnis/composer-normalize": "^2.15", + "mockery/mockery": "^1.3", + "paragonie/random-lib": "^2", + "php-mock/php-mock": "^2.2", + "php-mock/php-mock-mockery": "^1.3", + "php-parallel-lint/php-parallel-lint": "^1.1", + "phpbench/phpbench": "^1.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^8.5 || ^9", + "ramsey/composer-repl": "^1.4", + "slevomat/coding-standard": "^8.4", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.9" + }, + "suggest": { + "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", + "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", + "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", + "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "support": { + "issues": "https://github.com/ramsey/uuid/issues", + "source": "https://github.com/ramsey/uuid/tree/4.7.6" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", + "type": "tidelift" + } + ], + "time": "2024-04-27T21:32:50+00:00" + }, + { + "name": "react/promise", + "version": "v3.2.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "8a164643313c71354582dc850b42b33fa12a4b63" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/8a164643313c71354582dc850b42b33fa12a4b63", + "reference": "8a164643313c71354582dc850b42b33fa12a4b63", + "shasum": "" + }, + "require": { + "php": ">=7.1.0" + }, + "require-dev": { + "phpstan/phpstan": "1.10.39 || 1.4.10", + "phpunit/phpunit": "^9.6 || ^7.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "React\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], + "support": { + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v3.2.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-05-24T10:39:05+00:00" + }, + { + "name": "seld/jsonlint", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/jsonlint.git", + "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/1748aaf847fc731cfad7725aec413ee46f0cc3a2", + "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2", + "shasum": "" + }, + "require": { + "php": "^5.3 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^8.5.13" + }, + "bin": [ + "bin/jsonlint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Seld\\JsonLint\\": "src/Seld/JsonLint/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "JSON Linter", + "keywords": [ + "json", + "linter", + "parser", + "validator" + ], + "support": { + "issues": "https://github.com/Seldaek/jsonlint/issues", + "source": "https://github.com/Seldaek/jsonlint/tree/1.11.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint", + "type": "tidelift" + } + ], + "time": "2024-07-11T14:55:45+00:00" + }, + { + "name": "seld/phar-utils", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/phar-utils.git", + "reference": "ea2f4014f163c1be4c601b9b7bd6af81ba8d701c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/ea2f4014f163c1be4c601b9b7bd6af81ba8d701c", + "reference": "ea2f4014f163c1be4c601b9b7bd6af81ba8d701c", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Seld\\PharUtils\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "PHAR file format utilities, for when PHP phars you up", + "keywords": [ + "phar" + ], + "support": { + "issues": "https://github.com/Seldaek/phar-utils/issues", + "source": "https://github.com/Seldaek/phar-utils/tree/1.2.1" + }, + "time": "2022-08-31T10:31:18+00:00" + }, + { + "name": "seld/signal-handler", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/signal-handler.git", + "reference": "04a6112e883ad76c0ada8e4a9f7520bbfdb6bb98" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/signal-handler/zipball/04a6112e883ad76c0ada8e4a9f7520bbfdb6bb98", + "reference": "04a6112e883ad76c0ada8e4a9f7520bbfdb6bb98", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "require-dev": { + "phpstan/phpstan": "^1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1", + "phpstan/phpstan-strict-rules": "^1.3", + "phpunit/phpunit": "^7.5.20 || ^8.5.23", + "psr/log": "^1 || ^2 || ^3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Seld\\Signal\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Simple unix signal handler that silently fails where signals are not supported for easy cross-platform development", + "keywords": [ + "posix", + "sigint", + "signal", + "sigterm", + "unix" + ], + "support": { + "issues": "https://github.com/Seldaek/signal-handler/issues", + "source": "https://github.com/Seldaek/signal-handler/tree/2.0.2" + }, + "time": "2023-09-03T09:24:00+00:00" + }, + { + "name": "symfony/cache", + "version": "v7.2.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache.git", + "reference": "e7e983596b744c4539f31e79b0350a6cf5878a20" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache/zipball/e7e983596b744c4539f31e79b0350a6cf5878a20", + "reference": "e7e983596b744c4539f31e79b0350a6cf5878a20", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/cache": "^2.0|^3.0", + "psr/log": "^1.1|^2|^3", + "symfony/cache-contracts": "^2.5|^3", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/var-exporter": "^6.4|^7.0" + }, + "conflict": { + "doctrine/dbal": "<3.6", + "symfony/dependency-injection": "<6.4", + "symfony/http-kernel": "<6.4", + "symfony/var-dumper": "<6.4" + }, + "provide": { + "psr/cache-implementation": "2.0|3.0", + "psr/simple-cache-implementation": "1.0|2.0|3.0", + "symfony/cache-implementation": "1.1|2.0|3.0" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "doctrine/dbal": "^3.6|^4", + "predis/predis": "^1.1|^2.0", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "symfony/clock": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/filesystem": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Cache\\": "" + }, + "classmap": [ + "Traits/ValueWrapper.php" + ], + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides extended PSR-6, PSR-16 (and tags) implementations", + "homepage": "https://symfony.com", + "keywords": [ + "caching", + "psr6" + ], + "support": { + "source": "https://github.com/symfony/cache/tree/v7.2.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-07T08:08:50+00:00" + }, + { + "name": "symfony/cache-contracts", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache-contracts.git", + "reference": "15a4f8e5cd3bce9aeafc882b1acab39ec8de2c1b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/15a4f8e5cd3bce9aeafc882b1acab39ec8de2c1b", + "reference": "15a4f8e5cd3bce9aeafc882b1acab39ec8de2c1b", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/cache": "^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Cache\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to caching", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/cache-contracts/tree/v3.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/clock", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/clock.git", + "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/clock/zipball/b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", + "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/clock": "^1.0", + "symfony/polyfill-php83": "^1.28" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/now.php" + ], + "psr-4": { + "Symfony\\Component\\Clock\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Decouples applications from the system clock", + "homepage": "https://symfony.com", + "keywords": [ + "clock", + "psr20", + "time" + ], + "support": { + "source": "https://github.com/symfony/clock/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/console", + "version": "v7.2.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/fefcc18c0f5d0efe3ab3152f15857298868dc2c3", + "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^6.4|^7.0" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.2.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-11T03:49:26+00:00" + }, + { + "name": "symfony/css-selector", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/601a5ce9aaad7bf10797e3663faefce9e26c24e2", + "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Converts CSS selectors to XPath expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/css-selector/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/error-handler", + "version": "v7.2.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/error-handler.git", + "reference": "6150b89186573046167796fa5f3f76601d5145f8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/6150b89186573046167796fa5f3f76601d5145f8", + "reference": "6150b89186573046167796fa5f3f76601d5145f8", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/var-dumper": "^6.4|^7.0" + }, + "conflict": { + "symfony/deprecation-contracts": "<2.5", + "symfony/http-kernel": "<6.4" + }, + "require-dev": { + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0" + }, + "bin": [ + "Resources/bin/patch-type-declarations" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ErrorHandler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to manage errors and ease debugging PHP code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/error-handler/tree/v7.2.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-07T08:50:44+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/910c5db85a5356d0fea57680defec4e99eb9c8c1", + "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7642f5e970b672283b7823222ae8ef8bbc160b9f", + "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-10-25T15:15:23+00:00" + }, + { + "name": "symfony/finder", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "6de263e5868b9a137602dd1e33e4d48bfae99c49" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/6de263e5868b9a137602dd1e33e4d48bfae99c49", + "reference": "6de263e5868b9a137602dd1e33e4d48bfae99c49", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-10-23T06:56:12+00:00" + }, + { + "name": "symfony/http-client", + "version": "v7.2.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client.git", + "reference": "ff4df2b68d1c67abb9fef146e6540ea16b58d99e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client/zipball/ff4df2b68d1c67abb9fef146e6540ea16b58d99e", + "reference": "ff4df2b68d1c67abb9fef146e6540ea16b58d99e", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-client-contracts": "~3.4.4|^3.5.2", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "amphp/amp": "<2.5", + "php-http/discovery": "<1.15", + "symfony/http-foundation": "<6.4" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "1.0", + "symfony/http-client-implementation": "3.0" + }, + "require-dev": { + "amphp/http-client": "^4.2.1|^5.0", + "amphp/http-tunnel": "^1.0|^2.0", + "amphp/socket": "^1.1", + "guzzlehttp/promises": "^1.4|^2.0", + "nyholm/psr7": "^1.0", + "php-http/httplug": "^1.0|^2.0", + "psr/http-client": "^1.0", + "symfony/amphp-http-client-meta": "^1.0|^2.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/rate-limiter": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", + "homepage": "https://symfony.com", + "keywords": [ + "http" + ], + "support": { + "source": "https://github.com/symfony/http-client/tree/v7.2.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-07T08:50:44+00:00" + }, + { + "name": "symfony/http-client-contracts", + "version": "v3.5.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client-contracts.git", + "reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/ee8d807ab20fcb51267fdace50fbe3494c31e645", + "reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to HTTP clients", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-07T08:49:48+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "e88a66c3997859532bc2ddd6dd8f35aba2711744" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e88a66c3997859532bc2ddd6dd8f35aba2711744", + "reference": "e88a66c3997859532bc2ddd6dd8f35aba2711744", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php83": "^1.27" + }, + "conflict": { + "doctrine/dbal": "<3.6", + "symfony/cache": "<6.4.12|>=7.0,<7.1.5" + }, + "require-dev": { + "doctrine/dbal": "^3.6|^4", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^6.4.12|^7.1.5", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/rate-limiter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-13T18:58:46+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v7.2.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "d8ae58eecae44c8e66833e76cc50a4ad3c002d97" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/d8ae58eecae44c8e66833e76cc50a4ad3c002d97", + "reference": "d8ae58eecae44c8e66833e76cc50a4ad3c002d97", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/browser-kit": "<6.4", + "symfony/cache": "<6.4", + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/doctrine-bridge": "<6.4", + "symfony/form": "<6.4", + "symfony/http-client": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/mailer": "<6.4", + "symfony/messenger": "<6.4", + "symfony/translation": "<6.4", + "symfony/translation-contracts": "<2.5", + "symfony/twig-bridge": "<6.4", + "symfony/validator": "<6.4", + "symfony/var-dumper": "<6.4", + "twig/twig": "<3.12" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/browser-kit": "^6.4|^7.0", + "symfony/clock": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/css-selector": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/dom-crawler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/http-client-contracts": "^2.5|^3", + "symfony/process": "^6.4|^7.0", + "symfony/property-access": "^7.1", + "symfony/routing": "^6.4|^7.0", + "symfony/serializer": "^7.1", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/translation": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^6.4|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0", + "symfony/var-exporter": "^6.4|^7.0", + "twig/twig": "^3.12" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a structured process for converting a Request into a Response", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-kernel/tree/v7.2.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-11T12:09:10+00:00" + }, + { + "name": "symfony/mailer", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailer.git", + "reference": "e4d358702fb66e4c8a2af08e90e7271a62de39cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailer/zipball/e4d358702fb66e4c8a2af08e90e7271a62de39cc", + "reference": "e4d358702fb66e4c8a2af08e90e7271a62de39cc", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.1.10|^3|^4", + "php": ">=8.2", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/mime": "^7.2", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/messenger": "<6.4", + "symfony/mime": "<6.4", + "symfony/twig-bridge": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/twig-bridge": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps sending emails", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/mailer/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-25T15:21:05+00:00" + }, + { + "name": "symfony/mime", + "version": "v7.2.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "7f9617fcf15cb61be30f8b252695ed5e2bfac283" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/7f9617fcf15cb61be30f8b252695ed5e2bfac283", + "reference": "7f9617fcf15cb61be30f8b252695ed5e2bfac283", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/mailer": "<6.4", + "symfony/serializer": "<6.4.3|>7.0,<7.0.3" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/property-access": "^6.4|^7.0", + "symfony/property-info": "^6.4|^7.0", + "symfony/serializer": "^6.4.3|^7.0.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + "source": "https://github.com/symfony/mime/tree/v7.2.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-07T08:50:44+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c36586dcf89a12315939e00ec9b4474adcb1d773", + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f68c03565dcaaf25a890667542e8bd75fe7e5bb", + "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-php83", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491", + "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php83\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php83/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-uuid", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-uuid": "*" + }, + "suggest": { + "ext-uuid": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Uuid\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for uuid functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/process", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "d34b22ba9390ec19d2dd966c40aa9e8462f27a7e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/d34b22ba9390ec19d2dd966c40aa9e8462f27a7e", + "reference": "d34b22ba9390ec19d2dd966c40aa9e8462f27a7e", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-06T14:24:19+00:00" + }, + { + "name": "symfony/psr-http-message-bridge", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/psr-http-message-bridge.git", + "reference": "03f2f72319e7acaf2a9f6fcbe30ef17eec51594f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/03f2f72319e7acaf2a9f6fcbe30ef17eec51594f", + "reference": "03f2f72319e7acaf2a9f6fcbe30ef17eec51594f", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/http-message": "^1.0|^2.0", + "symfony/http-foundation": "^6.4|^7.0" + }, + "conflict": { + "php-http/discovery": "<1.15", + "symfony/http-kernel": "<6.4" + }, + "require-dev": { + "nyholm/psr7": "^1.1", + "php-http/discovery": "^1.15", + "psr/log": "^1.1.4|^2|^3", + "symfony/browser-kit": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/framework-bundle": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0" + }, + "type": "symfony-bridge", + "autoload": { + "psr-4": { + "Symfony\\Bridge\\PsrHttpMessage\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "PSR HTTP message bridge", + "homepage": "https://symfony.com", + "keywords": [ + "http", + "http-message", + "psr-17", + "psr-7" + ], + "support": { + "source": "https://github.com/symfony/psr-http-message-bridge/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-26T08:57:56+00:00" + }, + { + "name": "symfony/routing", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "e10a2450fa957af6c448b9b93c9010a4e4c0725e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/e10a2450fa957af6c448b9b93c9010a4e4c0725e", + "reference": "e10a2450fa957af6c448b9b93c9010a4e4c0725e", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/config": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/yaml": "<6.4" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Maps an HTTP request to a set of configuration variables", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "support": { + "source": "https://github.com/symfony/routing/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-25T11:08:51+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/string", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/446e0d146f991dde3e73f45f2c97a9faad773c82", + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.1", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-13T13:31:26+00:00" + }, + { + "name": "symfony/translation", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "dc89e16b44048ceecc879054e5b7f38326ab6cc5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/dc89e16b44048ceecc879054e5b7f38326ab6cc5", + "reference": "dc89e16b44048ceecc879054e5b7f38326ab6cc5", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.5|^3.0" + }, + "conflict": { + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<6.4", + "symfony/yaml": "<6.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "nikic/php-parser": "^4.18|^5.0", + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-12T20:47:56+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "4667ff3bd513750603a09c8dedbea942487fb07c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/4667ff3bd513750603a09c8dedbea942487fb07c", + "reference": "4667ff3bd513750603a09c8dedbea942487fb07c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v3.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/uid", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/uid.git", + "reference": "2d294d0c48df244c71c105a169d0190bfb080426" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/uid/zipball/2d294d0c48df244c71c105a169d0190bfb080426", + "reference": "2d294d0c48df244c71c105a169d0190bfb080426", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-uuid": "^1.15" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Uid\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to generate and represent UIDs", + "homepage": "https://symfony.com", + "keywords": [ + "UID", + "ulid", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/uid/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "c6a22929407dec8765d6e2b6ff85b800b245879c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/c6a22929407dec8765d6e2b6ff85b800b245879c", + "reference": "c6a22929407dec8765d6e2b6ff85b800b245879c", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/uid": "^6.4|^7.0", + "twig/twig": "^3.12" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-08T15:48:14+00:00" + }, + { + "name": "symfony/var-exporter", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-exporter.git", + "reference": "1a6a89f95a46af0f142874c9d650a6358d13070d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/1a6a89f95a46af0f142874c9d650a6358d13070d", + "reference": "1a6a89f95a46af0f142874c9d650a6358d13070d", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/property-access": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\VarExporter\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows exporting any serializable PHP data structure to plain PHP code", + "homepage": "https://symfony.com", + "keywords": [ + "clone", + "construct", + "export", + "hydrate", + "instantiate", + "lazy-loading", + "proxy", + "serialize" + ], + "support": { + "source": "https://github.com/symfony/var-exporter/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-10-18T07:58:17+00:00" + }, + { + "name": "symfony/yaml", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "099581e99f557e9f16b43c5916c26380b54abb22" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/099581e99f557e9f16b43c5916c26380b54abb22", + "reference": "099581e99f557e9f16b43c5916c26380b54abb22", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-10-23T06:56:12+00:00" + }, + { + "name": "thenorthmemory/xml", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/TheNorthMemory/xml.git", + "reference": "6f50c63450a0b098772423f8bdc3c4ad2c4c30bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TheNorthMemory/xml/zipball/6f50c63450a0b098772423f8bdc3c4ad2c4c30bb", + "reference": "6f50c63450a0b098772423f8bdc3c4ad2c4c30bb", + "shasum": "" + }, + "require": { + "ext-libxml": "*", + "ext-simplexml": "*", + "php": ">=7.1.2" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.89 || ^1.0", + "phpunit/phpunit": "^7.5 || ^8.5.16 || ^9.3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "TheNorthMemory\\Xml\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "James ZHANG", + "homepage": "https://github.com/TheNorthMemory" + } + ], + "description": "A wrapper of the XML parser and builder", + "homepage": "https://github.com/TheNorthMemory/xml", + "keywords": [ + "xml-builder", + "xml-parser" + ], + "support": { + "issues": "https://github.com/TheNorthMemory/xml/issues", + "source": "https://github.com/TheNorthMemory/xml/tree/1.1.1" + }, + "time": "2023-01-15T06:01:13+00:00" + }, + { + "name": "tijsverkoyen/css-to-inline-styles", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", + "reference": "0d72ac1c00084279c1816675284073c5a337c20d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/0d72ac1c00084279c1816675284073c5a337c20d", + "reference": "0d72ac1c00084279c1816675284073c5a337c20d", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "php": "^7.4 || ^8.0", + "symfony/css-selector": "^5.4 || ^6.0 || ^7.0" + }, + "require-dev": { + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^8.5.21 || ^9.5.10" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TijsVerkoyen\\CssToInlineStyles\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Tijs Verkoyen", + "email": "css_to_inline_styles@verkoyen.eu", + "role": "Developer" + } + ], + "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.", + "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", + "support": { + "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.3.0" + }, + "time": "2024-12-21T16:25:41+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "v5.6.1", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/a59a13791077fe3d44f90e7133eb68e7d22eaff2", + "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.1.3", + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.3", + "symfony/polyfill-ctype": "^1.24", + "symfony/polyfill-mbstring": "^1.24", + "symfony/polyfill-php80": "^1.24" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-filter": "*", + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "5.6-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "https://github.com/vlucas" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2024-07-20T21:52:34+00:00" + }, + { + "name": "voku/portable-ascii", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/voku/portable-ascii.git", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + }, + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" + }, + "type": "library", + "autoload": { + "psr-4": { + "voku\\": "src/voku/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lars Moelleken", + "homepage": "https://www.moelleken.org/" + } + ], + "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", + "homepage": "https://github.com/voku/portable-ascii", + "keywords": [ + "ascii", + "clean", + "php" + ], + "support": { + "issues": "https://github.com/voku/portable-ascii/issues", + "source": "https://github.com/voku/portable-ascii/tree/2.0.3" + }, + "funding": [ + { + "url": "https://www.paypal.me/moelleken", + "type": "custom" + }, + { + "url": "https://github.com/voku", + "type": "github" + }, + { + "url": "https://opencollective.com/portable-ascii", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", + "type": "tidelift" + } + ], + "time": "2024-11-21T01:49:47+00:00" + }, + { + "name": "w7corp/easywechat", + "version": "6.16.0", + "source": { + "type": "git", + "url": "https://github.com/w7corp/easywechat.git", + "reference": "8955713546a086851c3095de9fa8228617f026f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/w7corp/easywechat/zipball/8955713546a086851c3095de9fa8228617f026f4", + "reference": "8955713546a086851c3095de9fa8228617f026f4", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-fileinfo": "*", + "ext-libxml": "*", + "ext-openssl": "*", + "ext-simplexml": "*", + "ext-sodium": "*", + "nyholm/psr7": "^1.5", + "nyholm/psr7-server": "^1.0", + "overtrue/socialite": "^3.5.4|^4.0.1", + "php": ">=8.0.2", + "psr/http-client": "^1.0", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/mime": "^5.4|^6.0|^7.0", + "symfony/polyfill-php81": "^1.25", + "symfony/psr-http-message-bridge": "^2.1.2|^6.4.0|^7.1", + "thenorthmemory/xml": "^1.0" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "^1.0", + "laravel/pint": "^1.2", + "mikey179/vfsstream": "^1.6", + "mockery/mockery": "^1.4.4", + "phpstan/phpstan": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/var-dumper": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "EasyWeChat\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "overtrue", + "email": "anzhengchao@gmail.com" + } + ], + "description": "微信SDK", + "keywords": [ + "easywechat", + "sdk", + "wechat", + "weixin", + "weixin-sdk" + ], + "support": { + "issues": "https://github.com/w7corp/easywechat/issues", + "source": "https://github.com/w7corp/easywechat/tree/6.16.0" + }, + "funding": [ + { + "url": "https://github.com/overtrue", + "type": "github" + } + ], + "time": "2024-12-13T01:19:38+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + }, + { + "name": "wsdltophp/domhandler", + "version": "2.0.6", + "source": { + "type": "git", + "url": "https://github.com/WsdlToPhp/DomHandler.git", + "reference": "21eb8c020484508268a57a5393ed93ab938b7786" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/WsdlToPhp/DomHandler/zipball/21eb8c020484508268a57a5393ed93ab938b7786", + "reference": "21eb8c020484508268a57a5393ed93ab938b7786", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": ">=7.4" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~3.0", + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9" + }, + "type": "library", + "autoload": { + "psr-4": { + "WsdlToPhp\\DomHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mikaël DELSOL", + "email": "contact@wsdltophp.com", + "homepage": "https://www.wsdltophp.com", + "role": "Owner" + }, + { + "name": "phordijk", + "role": "Contributor" + } + ], + "description": "Decorative design pattern to ease DOM handling", + "homepage": "https://github.com/WsdlToPhp/DomHandler", + "keywords": [ + "Xpath", + "dom", + "php", + "xml" + ], + "support": { + "email": "contact@wsdltophp.com", + "issues": "https://github.com/WsdlToPhp/DomHandler/issues", + "source": "https://github.com/WsdlToPhp/DomHandler/tree/2.0.6" + }, + "time": "2022-03-24T20:28:45+00:00" + }, + { + "name": "wsdltophp/packagebase", + "version": "5.0.4", + "source": { + "type": "git", + "url": "https://github.com/WsdlToPhp/PackageBase.git", + "reference": "d2ac19eec63714da3ef8b8eecd529922cb44e26d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/WsdlToPhp/PackageBase/zipball/d2ac19eec63714da3ef8b8eecd529922cb44e26d", + "reference": "d2ac19eec63714da3ef8b8eecd529922cb44e26d", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-soap": "*", + "php": ">=7.4" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~3.0", + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9" + }, + "type": "library", + "autoload": { + "psr-4": { + "WsdlToPhp\\PackageBase\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mikaël DELSOL", + "email": "contact@wsdltophp.com", + "role": "Owner" + }, + { + "name": "Luke Rodgers", + "email": "lukerodgers90@gmail.com", + "role": "Contributor" + }, + { + "name": "Arthur Moore", + "email": "github@cd-net.net", + "role": "Contributor" + }, + { + "name": "Necati Yared Ozal", + "email": "mail@necatiozal.com", + "role": "Contributor" + }, + { + "name": "Gemorroj", + "email": "wapinet@mail.ru", + "role": "Contributor" + }, + { + "name": "hordijk", + "role": "Contributor" + }, + { + "name": "Jacob Dreesen", + "email": "jacob.dreesen@gmail.com", + "role": "Contributor" + }, + { + "name": "Karl Pierce", + "email": "kpierce@ratespecial.com", + "role": "Contributor" + }, + { + "name": "maurobn", + "role": "Contributor" + }, + { + "name": "Christo Yovev", + "role": "Contributor" + } + ], + "description": "Contains the base classes to be used by classes generated by wsdltophp/packagegenerator", + "homepage": "https://github.com/WsdlToPhp/PackageBase", + "keywords": [ + "models", + "php" + ], + "support": { + "email": "contact@wsdltophp.com", + "issues": "https://github.com/WsdlToPhp/PackageBase/issues", + "source": "https://github.com/WsdlToPhp/PackageBase/tree/5.0.4" + }, + "time": "2024-02-05T22:04:31+00:00" + }, + { + "name": "wsdltophp/packagegenerator", + "version": "4.1.13", + "source": { + "type": "git", + "url": "https://github.com/WsdlToPhp/PackageGenerator.git", + "reference": "fb5b00700be31694fb210b4b51fb9e9f9b7de137" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/WsdlToPhp/PackageGenerator/zipball/fb5b00700be31694fb210b4b51fb9e9f9b7de137", + "reference": "fb5b00700be31694fb210b4b51fb9e9f9b7de137", + "shasum": "" + }, + "require": { + "composer/composer": "^2.0", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-soap": "*", + "php": ">=7.4", + "symfony/console": "^4.0|^5.0|^6.0|^7.0", + "symfony/yaml": "^4.0|^5.0|^6.0|^7.0", + "wsdltophp/packagebase": "^5.0", + "wsdltophp/phpgenerator": "^4.0", + "wsdltophp/wsdlhandler": "^1.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.0", + "phpstan/phpstan": "^1.3", + "phpunit/phpunit": "^9", + "rector/rector": "^0.15.17" + }, + "type": "library", + "autoload": { + "psr-4": { + "WsdlToPhp\\PackageGenerator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mikaël DELSOL", + "email": "contact@wsdltophp.com", + "homepage": "https://www.wsdltophp.com", + "role": "Owner" + }, + { + "name": "Gemorroj", + "email": "wapinet@mail.ru", + "role": "Contributor" + }, + { + "name": "ceeram", + "email": "c33ram@gmail.com", + "role": "Contributor" + }, + { + "name": "GroxExMachine", + "email": "grox@mail.ru", + "role": "Contributor" + }, + { + "name": "Jan Zaeske", + "email": "mail@jzaeske.de", + "role": "Contributor" + }, + { + "name": "Tom Mottram", + "email": "tom@pay4later.com", + "role": "Contributor" + }, + { + "name": "Catirau Mihail", + "email": "ustmaestro@gmail.com", + "role": "Contributor" + }, + { + "name": "Alexander M. Turek", + "email": "me@derrabus.de", + "role": "Contributor" + }, + { + "name": "Valérian Girard", + "email": "waldo2188@gmail.com", + "role": "Contributor" + }, + { + "name": "hordijk", + "role": "Contributor" + }, + { + "name": "Andreas Möller", + "email": "am@localheinz.com", + "role": "Contributor" + }, + { + "name": "Andreas Kintzinger", + "role": "Contributor" + }, + { + "name": "Hendrik Luup", + "email": "hendrik@luup.info", + "role": "Contributor" + }, + { + "name": "Jacob Dreesen", + "email": "jacob.dreesen@gmail.com", + "role": "Contributor" + }, + { + "name": "Clifford Vickrey", + "email": "clifforddavidvickrey@gmail.com", + "role": "Contributor" + }, + { + "name": "Arnaud POINTET", + "role": "Contributor" + }, + { + "name": "dypa", + "role": "Contributor" + }, + { + "name": "gugglegum", + "email": "gugglegum@gmail.com", + "role": "Contributor" + }, + { + "name": "tbreuss", + "email": "thomasbreuss@gmx.ch", + "role": "Contributor" + }, + { + "name": "Alex Krátký", + "role": "Contributor" + } + ], + "description": "Generate hierarchical PHP classes based on a WSDL", + "homepage": "https://github.com/WsdlToPhp/PackageGenerator", + "keywords": [ + "generator", + "php", + "soap", + "wsdl" + ], + "support": { + "email": "contact@wsdltophp.com", + "issues": "https://github.com/WsdlToPhp/PackageGenerator/issues", + "source": "https://github.com/WsdlToPhp/PackageGenerator/tree/4.1.13" + }, + "time": "2024-06-21T04:10:25+00:00" + }, + { + "name": "wsdltophp/phpgenerator", + "version": "4.1.1", + "source": { + "type": "git", + "url": "https://github.com/WsdlToPhp/PhpGenerator.git", + "reference": "8dfdc4746590f10d9569ec3d4ec23b01955f1e91" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/WsdlToPhp/PhpGenerator/zipball/8dfdc4746590f10d9569ec3d4ec23b01955f1e91", + "reference": "8dfdc4746590f10d9569ec3d4ec23b01955f1e91", + "shasum": "" + }, + "require": { + "php": ">=7.4", + "phpstan/phpstan": "^1.5" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.0", + "phpunit/phpunit": "^9" + }, + "type": "library", + "autoload": { + "psr-4": { + "WsdlToPhp\\PhpGenerator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mikaël DELSOL", + "email": "contact@wsdltophp.com", + "role": "Owner" + } + ], + "description": "Generate php source file", + "homepage": "https://github.com/WsdlToPhp/PhpGenerator", + "keywords": [ + "generator", + "php" + ], + "support": { + "email": "contact@wsdltophp.com", + "issues": "https://github.com/WsdlToPhp/PhpGenerator/issues", + "source": "https://github.com/WsdlToPhp/PhpGenerator/tree/4.1.1" + }, + "time": "2022-03-24T21:22:21+00:00" + }, + { + "name": "wsdltophp/wsdlhandler", + "version": "1.0.6", + "source": { + "type": "git", + "url": "https://github.com/WsdlToPhp/WsdlHandler.git", + "reference": "0266d664555b6f0bdf24f0f3bb4fbc92cb3187de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/WsdlToPhp/WsdlHandler/zipball/0266d664555b6f0bdf24f0f3bb4fbc92cb3187de", + "reference": "0266d664555b6f0bdf24f0f3bb4fbc92cb3187de", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": ">=7.4", + "wsdltophp/domhandler": "~2.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~3.0", + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9" + }, + "type": "library", + "autoload": { + "psr-4": { + "WsdlToPhp\\WsdlHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mikaël DELSOL", + "email": "contact@wsdltophp.com", + "homepage": "https://www.wsdltophp.com", + "role": "Owner" + } + ], + "description": "Decorative design pattern to ease WSDL handling", + "homepage": "https://github.com/WsdlToPhp/WsdlHandler", + "keywords": [ + "Xpath", + "dom", + "php", + "wsdl", + "xml", + "xsd" + ], + "support": { + "email": "contact@wsdltophp.com", + "issues": "https://github.com/WsdlToPhp/WsdlHandler/issues", + "source": "https://github.com/WsdlToPhp/WsdlHandler/tree/1.0.6" + }, + "time": "2024-12-10T19:41:58+00:00" + } + ], + "packages-dev": [ + { + "name": "fakerphp/faker", + "version": "v1.24.1", + "source": { + "type": "git", + "url": "https://github.com/FakerPHP/Faker.git", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "conflict": { + "fzaninotto/faker": "*" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "doctrine/persistence": "^1.3 || ^2.0", + "ext-intl": "*", + "phpunit/phpunit": "^9.5.26", + "symfony/phpunit-bridge": "^5.4.16" + }, + "suggest": { + "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", + "ext-curl": "Required by Faker\\Provider\\Image to download images.", + "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", + "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", + "ext-mbstring": "Required for multibyte Unicode string functionality." + }, + "type": "library", + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "support": { + "issues": "https://github.com/FakerPHP/Faker/issues", + "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1" + }, + "time": "2024-11-21T13:46:39+00:00" + }, + { + "name": "filp/whoops", + "version": "2.16.0", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "befcdc0e5dce67252aa6322d82424be928214fa2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/befcdc0e5dce67252aa6322d82424be928214fa2", + "reference": "befcdc0e5dce67252aa6322d82424be928214fa2", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^4.0 || ^5.0" + }, + "suggest": { + "symfony/var-dumper": "Pretty print complex values better with var-dumper available", + "whoops/soap": "Formats errors as SOAP responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Whoops\\": "src/Whoops/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Filipe Dobreira", + "homepage": "https://github.com/filp", + "role": "Developer" + } + ], + "description": "php error handling for cool kids", + "homepage": "https://filp.github.io/whoops/", + "keywords": [ + "error", + "exception", + "handling", + "library", + "throwable", + "whoops" + ], + "support": { + "issues": "https://github.com/filp/whoops/issues", + "source": "https://github.com/filp/whoops/tree/2.16.0" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2024-09-25T12:00:00+00:00" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "shasum": "" + }, + "require": { + "php": "^5.3|^7.0|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" + }, + "time": "2020-07-09T08:09:16+00:00" + }, + { + "name": "laravel/pail", + "version": "v1.2.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/pail.git", + "reference": "353ac12134b98e2e7c3333d916bd3e523931e583" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pail/zipball/353ac12134b98e2e7c3333d916bd3e523931e583", + "reference": "353ac12134b98e2e7c3333d916bd3e523931e583", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "illuminate/console": "^10.24|^11.0", + "illuminate/contracts": "^10.24|^11.0", + "illuminate/log": "^10.24|^11.0", + "illuminate/process": "^10.24|^11.0", + "illuminate/support": "^10.24|^11.0", + "nunomaduro/termwind": "^1.15|^2.0", + "php": "^8.2", + "symfony/console": "^6.0|^7.0" + }, + "require-dev": { + "laravel/framework": "^10.24|^11.0", + "laravel/pint": "^1.13", + "orchestra/testbench-core": "^8.12|^9.0", + "pestphp/pest": "^2.20", + "pestphp/pest-plugin-type-coverage": "^2.3", + "phpstan/phpstan": "^1.10", + "symfony/var-dumper": "^6.3|^7.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Pail\\PailServiceProvider" + ] + }, + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Pail\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Easily delve into your Laravel application's log files directly from the command line.", + "homepage": "https://github.com/laravel/pail", + "keywords": [ + "laravel", + "logs", + "php", + "tail" + ], + "support": { + "issues": "https://github.com/laravel/pail/issues", + "source": "https://github.com/laravel/pail" + }, + "time": "2024-10-23T12:56:23+00:00" + }, + { + "name": "laravel/pint", + "version": "v1.18.3", + "source": { + "type": "git", + "url": "https://github.com/laravel/pint.git", + "reference": "cef51821608239040ab841ad6e1c6ae502ae3026" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pint/zipball/cef51821608239040ab841ad6e1c6ae502ae3026", + "reference": "cef51821608239040ab841ad6e1c6ae502ae3026", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^8.1.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.65.0", + "illuminate/view": "^10.48.24", + "larastan/larastan": "^2.9.11", + "laravel-zero/framework": "^10.4.0", + "mockery/mockery": "^1.6.12", + "nunomaduro/termwind": "^1.17.0", + "pestphp/pest": "^2.36.0" + }, + "bin": [ + "builds/pint" + ], + "type": "project", + "autoload": { + "psr-4": { + "App\\": "app/", + "Database\\Seeders\\": "database/seeders/", + "Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "An opinionated code formatter for PHP.", + "homepage": "https://laravel.com", + "keywords": [ + "format", + "formatter", + "lint", + "linter", + "php" + ], + "support": { + "issues": "https://github.com/laravel/pint/issues", + "source": "https://github.com/laravel/pint" + }, + "time": "2024-11-26T15:34:00+00:00" + }, + { + "name": "laravel/sail", + "version": "v1.39.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/sail.git", + "reference": "1a3c7291bc88de983b66688919a4d298d68ddec7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sail/zipball/1a3c7291bc88de983b66688919a4d298d68ddec7", + "reference": "1a3c7291bc88de983b66688919a4d298d68ddec7", + "shasum": "" + }, + "require": { + "illuminate/console": "^9.52.16|^10.0|^11.0", + "illuminate/contracts": "^9.52.16|^10.0|^11.0", + "illuminate/support": "^9.52.16|^10.0|^11.0", + "php": "^8.0", + "symfony/console": "^6.0|^7.0", + "symfony/yaml": "^6.0|^7.0" + }, + "require-dev": { + "orchestra/testbench": "^7.0|^8.0|^9.0", + "phpstan/phpstan": "^1.10" + }, + "bin": [ + "bin/sail" + ], + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Sail\\SailServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sail\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Docker files for running a basic Laravel application.", + "keywords": [ + "docker", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/sail/issues", + "source": "https://github.com/laravel/sail" + }, + "time": "2024-11-27T15:42:28+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.6.12", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": ">=7.3" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" + }, + "type": "library", + "autoload": { + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "https://github.com/padraic", + "role": "Author" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "docs": "https://docs.mockery.io/", + "issues": "https://github.com/mockery/mockery/issues", + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" + }, + "time": "2024-05-16T03:13:13+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.12.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2024-11-08T17:47:46+00:00" + }, + { + "name": "nunomaduro/collision", + "version": "v8.5.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/collision.git", + "reference": "f5c101b929c958e849a633283adff296ed5f38f5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/f5c101b929c958e849a633283adff296ed5f38f5", + "reference": "f5c101b929c958e849a633283adff296ed5f38f5", + "shasum": "" + }, + "require": { + "filp/whoops": "^2.16.0", + "nunomaduro/termwind": "^2.1.0", + "php": "^8.2.0", + "symfony/console": "^7.1.5" + }, + "conflict": { + "laravel/framework": "<11.0.0 || >=12.0.0", + "phpunit/phpunit": "<10.5.1 || >=12.0.0" + }, + "require-dev": { + "larastan/larastan": "^2.9.8", + "laravel/framework": "^11.28.0", + "laravel/pint": "^1.18.1", + "laravel/sail": "^1.36.0", + "laravel/sanctum": "^4.0.3", + "laravel/tinker": "^2.10.0", + "orchestra/testbench-core": "^9.5.3", + "pestphp/pest": "^2.36.0 || ^3.4.0", + "sebastian/environment": "^6.1.0 || ^7.2.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" + ] + }, + "branch-alias": { + "dev-8.x": "8.x-dev" + } + }, + "autoload": { + "files": [ + "./src/Adapters/Phpunit/Autoload.php" + ], + "psr-4": { + "NunoMaduro\\Collision\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Cli error handling for console/command-line PHP applications.", + "keywords": [ + "artisan", + "cli", + "command-line", + "console", + "error", + "handling", + "laravel", + "laravel-zero", + "php", + "symfony" + ], + "support": { + "issues": "https://github.com/nunomaduro/collision/issues", + "source": "https://github.com/nunomaduro/collision" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2024-10-15T16:06:32+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "11.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "418c59fd080954f8c4aa5631d9502ecda2387118" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/418c59fd080954f8c4aa5631d9502ecda2387118", + "reference": "418c59fd080954f8c4aa5631d9502ecda2387118", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.3.1", + "php": ">=8.2", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-text-template": "^4.0.1", + "sebastian/code-unit-reverse-lookup": "^4.0.1", + "sebastian/complexity": "^4.0.1", + "sebastian/environment": "^7.2.0", + "sebastian/lines-of-code": "^3.0.1", + "sebastian/version": "^5.0.2", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^11.5.0" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-12-11T12:34:27+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "5.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6", + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-27T05:02:59+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^11.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:07:44+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:08:43+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "7.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:09:35+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "11.5.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "153d0531b9f7e883c5053160cad6dd5ac28140b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/153d0531b9f7e883c5053160cad6dd5ac28140b3", + "reference": "153d0531b9f7e883c5053160cad6dd5ac28140b3", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.12.1", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.2", + "phpunit/php-code-coverage": "^11.0.8", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-invoker": "^5.0.1", + "phpunit/php-text-template": "^4.0.1", + "phpunit/php-timer": "^7.0.1", + "sebastian/cli-parser": "^3.0.2", + "sebastian/code-unit": "^3.0.2", + "sebastian/comparator": "^6.2.1", + "sebastian/diff": "^6.0.2", + "sebastian/environment": "^7.2.0", + "sebastian/exporter": "^6.3.0", + "sebastian/global-state": "^7.0.2", + "sebastian/object-enumerator": "^6.0.1", + "sebastian/type": "^5.1.0", + "sebastian/version": "^5.0.2", + "staabm/side-effects-detector": "^1.0.5" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.2" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2024-12-21T05:51:08+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:41:36+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "ee88b0cdbe74cf8dd3b54940ff17643c0d6543ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/ee88b0cdbe74cf8dd3b54940ff17643c0d6543ca", + "reference": "ee88b0cdbe74cf8dd3b54940ff17643c0d6543ca", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "security": "https://github.com/sebastianbergmann/code-unit/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-12-12T09:59:06+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:45:54+00:00" + }, + { + "name": "sebastian/comparator", + "version": "6.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "43d129d6a0f81c78bee378b46688293eb7ea3739" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/43d129d6a0f81c78bee378b46688293eb7ea3739", + "reference": "43d129d6a0f81c78bee378b46688293eb7ea3739", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/diff": "^6.0", + "sebastian/exporter": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/6.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-10-31T05:30:08+00:00" + }, + { + "name": "sebastian/complexity", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:49:50+00:00" + }, + { + "name": "sebastian/diff", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:53:05+00:00" + }, + { + "name": "sebastian/environment", + "version": "7.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5", + "reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/7.2.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:54:44+00:00" + }, + { + "name": "sebastian/exporter", + "version": "6.3.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/3473f61172093b2da7de1fb5782e1f24cc036dc3", + "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-12-05T09:17:50+00:00" + }, + { + "name": "sebastian/global-state", + "version": "7.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:57:36+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:58:38+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "6.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:00:13+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:01:32+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "694d156164372abbd149a4b85ccda2e4670c0e16" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/694d156164372abbd149a4b85ccda2e4670c0e16", + "reference": "694d156164372abbd149a4b85ccda2e4670c0e16", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:10:34+00:00" + }, + { + "name": "sebastian/type", + "version": "5.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "461b9c5da241511a2a0e8f240814fb23ce5c0aac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/461b9c5da241511a2a0e8f240814fb23ce5c0aac", + "reference": "461b9c5da241511a2a0e8f240814fb23ce5c0aac", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/5.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-09-17T13:12:04+00:00" + }, + { + "name": "sebastian/version", + "version": "5.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-10-09T05:16:32+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": "^8.2", + "ext-libxml": "*", + "ext-openssl": "*", + "ext-pdo": "*", + "ext-redis": "*", + "ext-simplexml": "*", + "ext-soap": "*" + }, + "platform-dev": {}, + "plugin-api-version": "2.6.0" +} diff --git a/config/app.php b/config/app.php new file mode 100644 index 0000000..f467267 --- /dev/null +++ b/config/app.php @@ -0,0 +1,126 @@ + env('APP_NAME', 'Laravel'), + + /* + |-------------------------------------------------------------------------- + | Application Environment + |-------------------------------------------------------------------------- + | + | This value determines the "environment" your application is currently + | running in. This may determine how you prefer to configure various + | services the application utilizes. Set this in your ".env" file. + | + */ + + 'env' => env('APP_ENV', 'production'), + + /* + |-------------------------------------------------------------------------- + | Application Debug Mode + |-------------------------------------------------------------------------- + | + | When your application is in debug mode, detailed error messages with + | stack traces will be shown on every error that occurs within your + | application. If disabled, a simple generic error page is shown. + | + */ + + 'debug' => (bool) env('APP_DEBUG', false), + + /* + |-------------------------------------------------------------------------- + | Application URL + |-------------------------------------------------------------------------- + | + | This URL is used by the console to properly generate URLs when using + | the Artisan command line tool. You should set this to the root of + | the application so that it's available within Artisan commands. + | + */ + + 'url' => env('APP_URL', 'http://localhost'), + + /* + |-------------------------------------------------------------------------- + | Application Timezone + |-------------------------------------------------------------------------- + | + | Here you may specify the default timezone for your application, which + | will be used by the PHP date and date-time functions. The timezone + | is set to "UTC" by default as it is suitable for most use cases. + | + */ + + 'timezone' => env('APP_TIMEZONE', 'UTC'), + + /* + |-------------------------------------------------------------------------- + | Application Locale Configuration + |-------------------------------------------------------------------------- + | + | The application locale determines the default locale that will be used + | by Laravel's translation / localization methods. This option can be + | set to any locale for which you plan to have translation strings. + | + */ + + 'locale' => env('APP_LOCALE', 'en'), + + 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'), + + 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'), + + /* + |-------------------------------------------------------------------------- + | Encryption Key + |-------------------------------------------------------------------------- + | + | This key is utilized by Laravel's encryption services and should be set + | to a random, 32 character string to ensure that all encrypted values + | are secure. You should do this prior to deploying the application. + | + */ + + 'cipher' => 'AES-256-CBC', + + 'key' => env('APP_KEY'), + + 'previous_keys' => [ + ...array_filter( + explode(',', env('APP_PREVIOUS_KEYS', '')) + ), + ], + + /* + |-------------------------------------------------------------------------- + | Maintenance Mode Driver + |-------------------------------------------------------------------------- + | + | These configuration options determine the driver used to determine and + | manage Laravel's "maintenance mode" status. The "cache" driver will + | allow maintenance mode to be controlled across multiple machines. + | + | Supported drivers: "file", "cache" + | + */ + + 'maintenance' => [ + 'driver' => env('APP_MAINTENANCE_DRIVER', 'file'), + 'store' => env('APP_MAINTENANCE_STORE', 'database'), + ], + +]; diff --git a/config/auth.php b/config/auth.php new file mode 100644 index 0000000..59214a1 --- /dev/null +++ b/config/auth.php @@ -0,0 +1,115 @@ + [ + 'guard' => env('AUTH_GUARD', 'web'), + 'passwords' => env('AUTH_PASSWORD_BROKER', 'users'), + ], + + /* + |-------------------------------------------------------------------------- + | Authentication Guards + |-------------------------------------------------------------------------- + | + | Next, you may define every authentication guard for your application. + | Of course, a great default configuration has been defined for you + | which utilizes session storage plus the Eloquent user provider. + | + | All authentication guards have a user provider, which defines how the + | users are actually retrieved out of your database or other storage + | system used by the application. Typically, Eloquent is utilized. + | + | Supported: "session" + | + */ + + 'guards' => [ + 'web' => [ + 'driver' => 'session', + 'provider' => 'users', + ] + ], + + /* + |-------------------------------------------------------------------------- + | User Providers + |-------------------------------------------------------------------------- + | + | All authentication guards have a user provider, which defines how the + | users are actually retrieved out of your database or other storage + | system used by the application. Typically, Eloquent is utilized. + | + | If you have multiple user tables or models you may configure multiple + | providers to represent the model / table. These providers may then + | be assigned to any extra authentication guards you have defined. + | + | Supported: "database", "eloquent" + | + */ + + 'providers' => [ + 'users' => [ + 'driver' => 'eloquent', + 'model' => env('AUTH_MODEL', App\Models\User::class), + ], + + // 'users' => [ + // 'driver' => 'database', + // 'table' => 'users', + // ], + ], + + /* + |-------------------------------------------------------------------------- + | Resetting Passwords + |-------------------------------------------------------------------------- + | + | These configuration options specify the behavior of Laravel's password + | reset functionality, including the table utilized for token storage + | and the user provider that is invoked to actually retrieve users. + | + | The expiry time is the number of minutes that each reset token will be + | considered valid. This security feature keeps tokens short-lived so + | they have less time to be guessed. You may change this as needed. + | + | The throttle setting is the number of seconds a user must wait before + | generating more password reset tokens. This prevents the user from + | quickly generating a very large amount of password reset tokens. + | + */ + + 'passwords' => [ + 'users' => [ + 'provider' => 'users', + 'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'), + 'expire' => 60, + 'throttle' => 60, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Password Confirmation Timeout + |-------------------------------------------------------------------------- + | + | Here you may define the amount of seconds before a password confirmation + | window expires and users are asked to re-enter their password via the + | confirmation screen. By default, the timeout lasts for three hours. + | + */ + + 'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800), + +]; diff --git a/config/cache.php b/config/cache.php new file mode 100644 index 0000000..925f7d2 --- /dev/null +++ b/config/cache.php @@ -0,0 +1,108 @@ + env('CACHE_STORE', 'database'), + + /* + |-------------------------------------------------------------------------- + | Cache Stores + |-------------------------------------------------------------------------- + | + | Here you may define all of the cache "stores" for your application as + | well as their drivers. You may even define multiple stores for the + | same cache driver to group types of items stored in your caches. + | + | Supported drivers: "array", "database", "file", "memcached", + | "redis", "dynamodb", "octane", "null" + | + */ + + 'stores' => [ + + 'array' => [ + 'driver' => 'array', + 'serialize' => false, + ], + + 'database' => [ + 'driver' => 'database', + 'connection' => env('DB_CACHE_CONNECTION'), + 'table' => env('DB_CACHE_TABLE', 'cache'), + 'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'), + 'lock_table' => env('DB_CACHE_LOCK_TABLE'), + ], + + 'file' => [ + 'driver' => 'file', + 'path' => storage_path('framework/cache/data'), + 'lock_path' => storage_path('framework/cache/data'), + ], + + 'memcached' => [ + 'driver' => 'memcached', + 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), + 'sasl' => [ + env('MEMCACHED_USERNAME'), + env('MEMCACHED_PASSWORD'), + ], + 'options' => [ + // Memcached::OPT_CONNECT_TIMEOUT => 2000, + ], + 'servers' => [ + [ + 'host' => env('MEMCACHED_HOST', '127.0.0.1'), + 'port' => env('MEMCACHED_PORT', 11211), + 'weight' => 100, + ], + ], + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => env('REDIS_CACHE_CONNECTION', 'cache'), + 'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'), + ], + + 'dynamodb' => [ + 'driver' => 'dynamodb', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'), + 'endpoint' => env('DYNAMODB_ENDPOINT'), + ], + + 'octane' => [ + 'driver' => 'octane', + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Cache Key Prefix + |-------------------------------------------------------------------------- + | + | When utilizing the APC, database, memcached, Redis, and DynamoDB cache + | stores, there might be other applications using the same cache. For + | that reason, you may prefix every cache key to avoid collisions. + | + */ + + 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'), + +]; diff --git a/config/custom.php b/config/custom.php new file mode 100644 index 0000000..7bf82d9 --- /dev/null +++ b/config/custom.php @@ -0,0 +1,10 @@ + 5 +]; diff --git a/config/database.php b/config/database.php new file mode 100644 index 0000000..edaa45f --- /dev/null +++ b/config/database.php @@ -0,0 +1,181 @@ + env('DB_CONNECTION', 'sqlite'), + + /* + |-------------------------------------------------------------------------- + | Database Connections + |-------------------------------------------------------------------------- + | + | Below are all of the database connections defined for your application. + | An example configuration is provided for each database system which + | is supported by Laravel. You're free to add / remove connections. + | + */ + + 'connections' => [ + + 'sqlite' => [ + 'driver' => 'sqlite', + 'url' => env('DB_URL'), + 'database' => env('DB_DATABASE', database_path('database.sqlite')), + 'prefix' => '', + 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), + 'busy_timeout' => null, + 'journal_mode' => null, + 'synchronous' => null, + ], + + 'mysql' => [ + 'driver' => 'mysql', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => env('DB_CHARSET', 'utf8mb4'), + 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), + 'prefix' => env('DB_PREFIX', ''), + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), + ]) : [], + ], + + 'mariadb' => [ + 'driver' => 'mariadb', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => env('DB_CHARSET', 'utf8mb4'), + 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), + ]) : [], + ], + + 'pgsql' => [ + 'driver' => 'pgsql', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '5432'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => env('DB_CHARSET', 'utf8'), + 'prefix' => '', + 'prefix_indexes' => true, + 'search_path' => 'public', + 'sslmode' => 'prefer', + ], + + 'sqlsrv' => [ + 'driver' => 'sqlsrv', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', 'localhost'), + 'port' => env('DB_PORT', '1433'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => env('DB_CHARSET', 'utf8'), + 'prefix' => '', + 'prefix_indexes' => true, + // 'encrypt' => env('DB_ENCRYPT', 'yes'), + // 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'), + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Migration Repository Table + |-------------------------------------------------------------------------- + | + | This table keeps track of all the migrations that have already run for + | your application. Using this information, we can determine which of + | the migrations on disk haven't actually been run on the database. + | + */ + + 'migrations' => [ + 'table' => 'migrations', + 'update_date_on_publish' => true, + ], + + /* + |-------------------------------------------------------------------------- + | Redis Databases + |-------------------------------------------------------------------------- + | + | Redis is an open source, fast, and advanced key-value store that also + | provides a richer body of commands than a typical key-value system + | such as Memcached. You may define your connection settings here. + | + */ + + 'redis' => [ + + 'client' => env('REDIS_CLIENT', 'phpredis'), + + 'options' => [ + 'cluster' => env('REDIS_CLUSTER', 'redis'), + 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), + ], + + 'default' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_DB', '0'), + ], + + 'cache' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_CACHE_DB', '1'), + ], + + 'session' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_SESSION_DB', '2'), + ], + ], + +]; diff --git a/config/filesystems.php b/config/filesystems.php new file mode 100644 index 0000000..b564035 --- /dev/null +++ b/config/filesystems.php @@ -0,0 +1,77 @@ + env('FILESYSTEM_DISK', 'local'), + + /* + |-------------------------------------------------------------------------- + | Filesystem Disks + |-------------------------------------------------------------------------- + | + | Below you may configure as many filesystem disks as necessary, and you + | may even configure multiple disks for the same driver. Examples for + | most supported storage drivers are configured here for reference. + | + | Supported drivers: "local", "ftp", "sftp", "s3" + | + */ + + 'disks' => [ + + 'local' => [ + 'driver' => 'local', + 'root' => storage_path('app/private'), + 'serve' => true, + 'throw' => false, + ], + + 'public' => [ + 'driver' => 'local', + 'root' => storage_path('app/public'), + 'url' => env('APP_URL').'/storage', + 'visibility' => 'public', + 'throw' => false, + ], + + 's3' => [ + 'driver' => 's3', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION'), + 'bucket' => env('AWS_BUCKET'), + 'url' => env('AWS_URL'), + 'endpoint' => env('AWS_ENDPOINT'), + 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), + 'throw' => false, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Symbolic Links + |-------------------------------------------------------------------------- + | + | Here you may configure the symbolic links that will be created when the + | `storage:link` Artisan command is executed. The array keys should be + | the locations of the links and the values should be their targets. + | + */ + + 'links' => [ + public_path('storage') => storage_path('app/public'), + ], + +]; diff --git a/config/hisservice.php b/config/hisservice.php new file mode 100644 index 0000000..fada570 --- /dev/null +++ b/config/hisservice.php @@ -0,0 +1,14 @@ + [ + 'url' => '', + 'location' => '', + 'service_type_namespace' => 'HisSoapService\ServiceType\\', + 'struct_type_namespace' => 'HisSoapService\StructType\\', + // 不记录日志的数组请求 + 'not_log_arr' => [], + // 是否模拟数据 + 'is_mock' => true, + ], +]; diff --git a/config/logging.php b/config/logging.php new file mode 100644 index 0000000..7393804 --- /dev/null +++ b/config/logging.php @@ -0,0 +1,169 @@ + env('LOG_CHANNEL', 'stack'), + + /* + |-------------------------------------------------------------------------- + | Deprecations Log Channel + |-------------------------------------------------------------------------- + | + | This option controls the log channel that should be used to log warnings + | regarding deprecated PHP and library features. This allows you to get + | your application ready for upcoming major versions of dependencies. + | + */ + + 'deprecations' => [ + 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), + 'trace' => env('LOG_DEPRECATIONS_TRACE', false), + ], + + /* + |-------------------------------------------------------------------------- + | Log Channels + |-------------------------------------------------------------------------- + | + | Here you may configure the log channels for your application. Laravel + | utilizes the Monolog PHP logging library, which includes a variety + | of powerful log handlers and formatters that you're free to use. + | + | Available drivers: "single", "daily", "slack", "syslog", + | "errorlog", "monolog", "custom", "stack" + | + */ + + 'channels' => [ + + 'stack' => [ + 'driver' => 'stack', + 'channels' => explode(',', env('LOG_STACK', 'single')), + 'ignore_exceptions' => false, + ], + + 'single' => [ + 'driver' => 'single', + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + 'replace_placeholders' => true, + ], + + 'daily' => [ + 'driver' => 'daily', + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + 'days' => env('LOG_DAILY_DAYS', 14), + 'replace_placeholders' => true, + ], + + 'slack' => [ + 'driver' => 'slack', + 'url' => env('LOG_SLACK_WEBHOOK_URL'), + 'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'), + 'emoji' => env('LOG_SLACK_EMOJI', ':boom:'), + 'level' => env('LOG_LEVEL', 'critical'), + 'replace_placeholders' => true, + ], + + 'papertrail' => [ + 'driver' => 'monolog', + 'level' => env('LOG_LEVEL', 'debug'), + 'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class), + 'handler_with' => [ + 'host' => env('PAPERTRAIL_URL'), + 'port' => env('PAPERTRAIL_PORT'), + 'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'), + ], + 'processors' => [PsrLogMessageProcessor::class], + ], + + 'stderr' => [ + 'driver' => 'monolog', + 'level' => env('LOG_LEVEL', 'debug'), + 'handler' => StreamHandler::class, + 'formatter' => env('LOG_STDERR_FORMATTER'), + 'with' => [ + 'stream' => 'php://stderr', + ], + 'processors' => [PsrLogMessageProcessor::class], + ], + + 'syslog' => [ + 'driver' => 'syslog', + 'level' => env('LOG_LEVEL', 'debug'), + 'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER), + 'replace_placeholders' => true, + ], + + 'errorlog' => [ + 'driver' => 'errorlog', + 'level' => env('LOG_LEVEL', 'debug'), + 'replace_placeholders' => true, + ], + + 'null' => [ + 'driver' => 'monolog', + 'handler' => NullHandler::class, + ], + + 'emergency' => [ + 'path' => storage_path('logs/laravel.log'), + ], + + // Default + 'default' => [ + 'driver' => 'custom', + 'via' => GeneralDailyLogger::class, + 'service_type' => 'Default', + 'level' => Level::Info, + 'max_files' => 30, + ], + + // 通用日志 + 'genera' => [ + 'driver' => 'custom', + 'via' => GeneralDailyLogger::class, + 'service_type' => 'GeneraLog', + 'level' => Level::Info, + 'max_files' => 30, + ], + + // HisSoap + 'his_soap' => [ + 'driver' => 'custom', + 'via' => GeneralDailyLogger::class, + 'service_type' => 'HisSoapLog', + 'level' => Level::Info, + 'max_files' => 360, + ], + + // 退费相关日志 + 'refund' => [ + 'driver' => 'custom', + 'via' => GeneralDailyLogger::class, + 'service_type' => 'RefundLog', + 'level' => Level::Info, + 'max_files' => 0, + ], + ], + +]; diff --git a/config/mail.php b/config/mail.php new file mode 100644 index 0000000..df13d3d --- /dev/null +++ b/config/mail.php @@ -0,0 +1,116 @@ + env('MAIL_MAILER', 'log'), + + /* + |-------------------------------------------------------------------------- + | Mailer Configurations + |-------------------------------------------------------------------------- + | + | Here you may configure all of the mailers used by your application plus + | their respective settings. Several examples have been configured for + | you and you are free to add your own as your application requires. + | + | Laravel supports a variety of mail "transport" drivers that can be used + | when delivering an email. You may specify which one you're using for + | your mailers below. You may also add additional mailers if needed. + | + | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", + | "postmark", "resend", "log", "array", + | "failover", "roundrobin" + | + */ + + 'mailers' => [ + + 'smtp' => [ + 'transport' => 'smtp', + 'url' => env('MAIL_URL'), + 'host' => env('MAIL_HOST', '127.0.0.1'), + 'port' => env('MAIL_PORT', 2525), + 'encryption' => env('MAIL_ENCRYPTION', 'tls'), + 'username' => env('MAIL_USERNAME'), + 'password' => env('MAIL_PASSWORD'), + 'timeout' => null, + 'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url(env('APP_URL', 'http://localhost'), PHP_URL_HOST)), + ], + + 'ses' => [ + 'transport' => 'ses', + ], + + 'postmark' => [ + 'transport' => 'postmark', + // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'), + // 'client' => [ + // 'timeout' => 5, + // ], + ], + + 'resend' => [ + 'transport' => 'resend', + ], + + 'sendmail' => [ + 'transport' => 'sendmail', + 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'), + ], + + 'log' => [ + 'transport' => 'log', + 'channel' => env('MAIL_LOG_CHANNEL'), + ], + + 'array' => [ + 'transport' => 'array', + ], + + 'failover' => [ + 'transport' => 'failover', + 'mailers' => [ + 'smtp', + 'log', + ], + ], + + 'roundrobin' => [ + 'transport' => 'roundrobin', + 'mailers' => [ + 'ses', + 'postmark', + ], + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Global "From" Address + |-------------------------------------------------------------------------- + | + | You may wish for all emails sent by your application to be sent from + | the same address. Here you may specify a name and address that is + | used globally for all emails that are sent by your application. + | + */ + + 'from' => [ + 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), + 'name' => env('MAIL_FROM_NAME', 'Example'), + ], + +]; diff --git a/config/payment.php b/config/payment.php new file mode 100644 index 0000000..a74ff89 --- /dev/null +++ b/config/payment.php @@ -0,0 +1,57 @@ + 120, + + // 微信 + 'wechat' => [ + // 公众号 APPID + 'app_id' => '', + + // 小程序 APPID + 'miniapp_id' => '', + + // APP 引用的 APPID + 'appid' => '', + + // 子商户 APP APPID + 'sub_appid' => '', + + // 微信支付分配的微信商户号 + 'mch_id' => '', + + // 子商户商户号 + 'sub_mch_id' => '', + + // 微信支付异步通知地址 + 'notify_url' => '', + + // 微信支付签名秘钥 + 'key' => 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB', + + // 客户端证书路径,退款、红包等需要用到。请填写绝对路径,linux 请确保权限问题。pem 格式。 + 'cert_client' => base_path('pem'. DIRECTORY_SEPARATOR. 'apiclient_cert.pem'), + + // 客户端秘钥路径,退款、红包等需要用到。请填写绝对路径,linux 请确保权限问题。pem 格式。 + 'cert_key' => base_path('pem'. DIRECTORY_SEPARATOR. 'apiclient_key.pem'), + + // optional 服务商模式 + 'mode' => 'service', + + // optional,默认 warning;日志路径为:sys_get_temp_dir().'/logs/yansongda.pay.log' + 'log' => [ // optional + 'file' => storage_path('/logs/wechat.log'), + 'level' => 'info', // 建议生产环境等级调整为 info,开发环境为 debug + 'type' => 'daily', // optional, 可选 daily, daily 时将按时间自动划分文件. + ], + + // http, 请求option配置 更多配置项请参考 Guzzle 文档 + 'http' => [ + 'verify' => false, + 'timeout' => 20, // 超时时间 20s + 'connect_timeout' => 5.0, // 连接时间 5s + ], + ], + +]; diff --git a/config/queue.php b/config/queue.php new file mode 100644 index 0000000..116bd8d --- /dev/null +++ b/config/queue.php @@ -0,0 +1,112 @@ + env('QUEUE_CONNECTION', 'database'), + + /* + |-------------------------------------------------------------------------- + | Queue Connections + |-------------------------------------------------------------------------- + | + | Here you may configure the connection options for every queue backend + | used by your application. An example configuration is provided for + | each backend supported by Laravel. You're also free to add more. + | + | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null" + | + */ + + 'connections' => [ + + 'sync' => [ + 'driver' => 'sync', + ], + + 'database' => [ + 'driver' => 'database', + 'connection' => env('DB_QUEUE_CONNECTION'), + 'table' => env('DB_QUEUE_TABLE', 'jobs'), + 'queue' => env('DB_QUEUE', 'default'), + 'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90), + 'after_commit' => false, + ], + + 'beanstalkd' => [ + 'driver' => 'beanstalkd', + 'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'), + 'queue' => env('BEANSTALKD_QUEUE', 'default'), + 'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90), + 'block_for' => 0, + 'after_commit' => false, + ], + + 'sqs' => [ + 'driver' => 'sqs', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), + 'queue' => env('SQS_QUEUE', 'default'), + 'suffix' => env('SQS_SUFFIX'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'after_commit' => false, + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), + 'queue' => env('REDIS_QUEUE', 'default'), + 'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90), + 'block_for' => null, + 'after_commit' => false, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Job Batching + |-------------------------------------------------------------------------- + | + | The following options configure the database and table that store job + | batching information. These options can be updated to any database + | connection and table which has been defined by your application. + | + */ + + 'batching' => [ + 'database' => env('DB_CONNECTION', 'sqlite'), + 'table' => 'job_batches', + ], + + /* + |-------------------------------------------------------------------------- + | Failed Queue Jobs + |-------------------------------------------------------------------------- + | + | These options configure the behavior of failed queue job logging so you + | can control how and where failed jobs are stored. Laravel ships with + | support for storing failed jobs in a simple file or in a database. + | + | Supported drivers: "database-uuids", "dynamodb", "file", "null" + | + */ + + 'failed' => [ + 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), + 'database' => env('DB_CONNECTION', 'sqlite'), + 'table' => 'failed_jobs', + ], + +]; diff --git a/config/sanctum.php b/config/sanctum.php new file mode 100644 index 0000000..764a82f --- /dev/null +++ b/config/sanctum.php @@ -0,0 +1,83 @@ + explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( + '%s%s', + 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', + Sanctum::currentApplicationUrlWithPort() + ))), + + /* + |-------------------------------------------------------------------------- + | Sanctum Guards + |-------------------------------------------------------------------------- + | + | This array contains the authentication guards that will be checked when + | Sanctum is trying to authenticate a request. If none of these guards + | are able to authenticate the request, Sanctum will use the bearer + | token that's present on an incoming request for authentication. + | + */ + + 'guard' => ['web'], + + /* + |-------------------------------------------------------------------------- + | Expiration Minutes + |-------------------------------------------------------------------------- + | + | This value controls the number of minutes until an issued token will be + | considered expired. This will override any values set in the token's + | "expires_at" attribute, but first-party sessions are not affected. + | + */ + + 'expiration' => null, + + /* + |-------------------------------------------------------------------------- + | Token Prefix + |-------------------------------------------------------------------------- + | + | Sanctum can prefix new tokens in order to take advantage of numerous + | security scanning initiatives maintained by open source platforms + | that notify developers if they commit tokens into repositories. + | + | See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning + | + */ + + 'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''), + + /* + |-------------------------------------------------------------------------- + | Sanctum Middleware + |-------------------------------------------------------------------------- + | + | When authenticating your first-party SPA with Sanctum you may need to + | customize some of the middleware Sanctum uses while processing the + | request. You may change the middleware listed below as required. + | + */ + + 'middleware' => [ + 'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class, + 'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class, + 'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, + ], + +]; diff --git a/config/services.php b/config/services.php new file mode 100644 index 0000000..27a3617 --- /dev/null +++ b/config/services.php @@ -0,0 +1,38 @@ + [ + 'token' => env('POSTMARK_TOKEN'), + ], + + 'ses' => [ + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + ], + + 'resend' => [ + 'key' => env('RESEND_KEY'), + ], + + 'slack' => [ + 'notifications' => [ + 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'), + 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'), + ], + ], + +]; diff --git a/config/session.php b/config/session.php new file mode 100644 index 0000000..f0b6541 --- /dev/null +++ b/config/session.php @@ -0,0 +1,217 @@ + env('SESSION_DRIVER', 'database'), + + /* + |-------------------------------------------------------------------------- + | Session Lifetime + |-------------------------------------------------------------------------- + | + | Here you may specify the number of minutes that you wish the session + | to be allowed to remain idle before it expires. If you want them + | to expire immediately when the browser is closed then you may + | indicate that via the expire_on_close configuration option. + | + */ + + 'lifetime' => env('SESSION_LIFETIME', 120), + + 'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false), + + /* + |-------------------------------------------------------------------------- + | Session Encryption + |-------------------------------------------------------------------------- + | + | This option allows you to easily specify that all of your session data + | should be encrypted before it's stored. All encryption is performed + | automatically by Laravel and you may use the session like normal. + | + */ + + 'encrypt' => env('SESSION_ENCRYPT', false), + + /* + |-------------------------------------------------------------------------- + | Session File Location + |-------------------------------------------------------------------------- + | + | When utilizing the "file" session driver, the session files are placed + | on disk. The default storage location is defined here; however, you + | are free to provide another location where they should be stored. + | + */ + + 'files' => storage_path('framework/sessions'), + + /* + |-------------------------------------------------------------------------- + | Session Database Connection + |-------------------------------------------------------------------------- + | + | When using the "database" or "redis" session drivers, you may specify a + | connection that should be used to manage these sessions. This should + | correspond to a connection in your database configuration options. + | + */ + + 'connection' => env('SESSION_CONNECTION'), + + /* + |-------------------------------------------------------------------------- + | Session Database Table + |-------------------------------------------------------------------------- + | + | When using the "database" session driver, you may specify the table to + | be used to store sessions. Of course, a sensible default is defined + | for you; however, you're welcome to change this to another table. + | + */ + + 'table' => env('SESSION_TABLE', 'sessions'), + + /* + |-------------------------------------------------------------------------- + | Session Cache Store + |-------------------------------------------------------------------------- + | + | When using one of the framework's cache driven session backends, you may + | define the cache store which should be used to store the session data + | between requests. This must match one of your defined cache stores. + | + | Affects: "apc", "dynamodb", "memcached", "redis" + | + */ + + 'store' => env('SESSION_STORE'), + + /* + |-------------------------------------------------------------------------- + | Session Sweeping Lottery + |-------------------------------------------------------------------------- + | + | Some session drivers must manually sweep their storage location to get + | rid of old sessions from storage. Here are the chances that it will + | happen on a given request. By default, the odds are 2 out of 100. + | + */ + + 'lottery' => [2, 100], + + /* + |-------------------------------------------------------------------------- + | Session Cookie Name + |-------------------------------------------------------------------------- + | + | Here you may change the name of the session cookie that is created by + | the framework. Typically, you should not need to change this value + | since doing so does not grant a meaningful security improvement. + | + */ + + 'cookie' => env( + 'SESSION_COOKIE', + Str::slug(env('APP_NAME', 'laravel'), '_').'_session' + ), + + /* + |-------------------------------------------------------------------------- + | Session Cookie Path + |-------------------------------------------------------------------------- + | + | The session cookie path determines the path for which the cookie will + | be regarded as available. Typically, this will be the root path of + | your application, but you're free to change this when necessary. + | + */ + + 'path' => env('SESSION_PATH', '/'), + + /* + |-------------------------------------------------------------------------- + | Session Cookie Domain + |-------------------------------------------------------------------------- + | + | This value determines the domain and subdomains the session cookie is + | available to. By default, the cookie will be available to the root + | domain and all subdomains. Typically, this shouldn't be changed. + | + */ + + 'domain' => env('SESSION_DOMAIN'), + + /* + |-------------------------------------------------------------------------- + | HTTPS Only Cookies + |-------------------------------------------------------------------------- + | + | By setting this option to true, session cookies will only be sent back + | to the server if the browser has a HTTPS connection. This will keep + | the cookie from being sent to you when it can't be done securely. + | + */ + + 'secure' => env('SESSION_SECURE_COOKIE'), + + /* + |-------------------------------------------------------------------------- + | HTTP Access Only + |-------------------------------------------------------------------------- + | + | Setting this value to true will prevent JavaScript from accessing the + | value of the cookie and the cookie will only be accessible through + | the HTTP protocol. It's unlikely you should disable this option. + | + */ + + 'http_only' => env('SESSION_HTTP_ONLY', true), + + /* + |-------------------------------------------------------------------------- + | Same-Site Cookies + |-------------------------------------------------------------------------- + | + | This option determines how your cookies behave when cross-site requests + | take place, and can be used to mitigate CSRF attacks. By default, we + | will set this value to "lax" to permit secure cross-site requests. + | + | See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value + | + | Supported: "lax", "strict", "none", null + | + */ + + 'same_site' => env('SESSION_SAME_SITE', 'lax'), + + /* + |-------------------------------------------------------------------------- + | Partitioned Cookies + |-------------------------------------------------------------------------- + | + | Setting this value to true will tie the cookie to the top-level site for + | a cross-site context. Partitioned cookies are accepted by the browser + | when flagged "secure" and the Same-Site attribute is set to "none". + | + */ + + 'partitioned' => env('SESSION_PARTITIONED_COOKIE', false), + +]; diff --git a/config/wechat.php b/config/wechat.php new file mode 100644 index 0000000..f18865c --- /dev/null +++ b/config/wechat.php @@ -0,0 +1,159 @@ + [ + /* + |-------------------------------------------------------------------------- + | WeChat Official Account 配置 + |-------------------------------------------------------------------------- + | + | 微信公众号对应配置数据 + | 详见:https://easywechat.com/6.x/official-account/index.html + | + */ + 'app_id' => env('WECHAT_OFFICIAL_APP_ID', ''), + 'secret' => env('WECHAT_OFFICIAL_APP_SECRET', ''), + 'token' => env('WECHAT_OFFICIAL_TOKEN', ''), + 'aes_key' => env('WECHAT_OFFICIAL_AES_KEY', ''), + + /* + |-------------------------------------------------------------------------- + | OAuth 配置 + |-------------------------------------------------------------------------- + | + | scopes:公众平台(snsapi_userinfo / snsapi_base),开放平台:snsapi_login + | callback:OAuth授权完成后的回调页地址 + | + */ + 'oauth' => [ + 'scopes' => ['snsapi_userinfo'], + 'callback' => env('WECHAT_OAUTH_CALLBACK_URL', ''), + ], + + /* + |-------------------------------------------------------------------------- + | Http 配置 + |-------------------------------------------------------------------------- + | + | 接口请求相关配置,超时时间等,具体可用参数请参考: + | https://github.com/symfony/symfony/blob/5.3/src/Symfony/Contracts/HttpClient/HttpClientInterface.php + | + */ + 'http' => [ + 'timeout' => 5.0, + // 如果你在国外想要覆盖默认的 url 的时候才使用,根据不同的模块配置不同的 uri + // 'base_uri' => 'https://api.weixin.qq.com/', + + 'retry' => true, // 使用默认重试配置 + /* 'retry' => [ + // 仅以下状态码重试 + 'http_codes' => [429, 500] + // 最大重试次数 + 'max_retries' => 3, + // 请求间隔 (毫秒) + 'delay' => 1000, + // 如果设置,每次重试的等待时间都会增加这个系数 + // (例如. 首次:1000ms; 第二次: 3 * 1000ms; etc.) + 'multiplier' => 3 + ],*/ + ], + ], + + 'mini' => [ + /* + |-------------------------------------------------------------------------- + | WeChat Mini Program 配置 + |-------------------------------------------------------------------------- + | + | 微信小程序对应配置数据 + | 详见:https://easywechat.com/6.x/mini-app/index.html + | + */ + 'app_id' => env('WECHAT_MINI_APP_ID', 'wxccd0c4f642673a6d'), + 'secret' => env('WECHAT_MINI_APP_SECRET', '73f66753cf45336186f8640b6673d5fb'), + 'token' => env('WECHAT_MINI_TOKEN', ''), + 'aes_key' => env('WECHAT_MINI_AES_KEY', ''), + + /* + |-------------------------------------------------------------------------- + | Http 配置 + |-------------------------------------------------------------------------- + | + | 接口请求相关配置,超时时间等,具体可用参数请参考: + | https://github.com/symfony/symfony/blob/5.3/src/Symfony/Contracts/HttpClient/HttpClientInterface.php + | + */ + 'http' => [ + // 默认不抛出异常 + 'throw' => false, + // 超时时间 + 'timeout' => 5.0, + // 如果你在国外想要覆盖默认的 url 的时候才使用,根据不同的模块配置不同的 uri + // 'base_uri' => 'https://api.weixin.qq.com/', + + 'retry' => true, // 使用默认重试配置 + /* 'retry' => [ + // 仅以下状态码重试 + 'http_codes' => [429, 500] + // 最大重试次数 + 'max_retries' => 3, + // 请求间隔 (毫秒) + 'delay' => 1000, + // 如果设置,每次重试的等待时间都会增加这个系数 + // (例如. 首次:1000ms; 第二次: 3 * 1000ms; etc.) + 'multiplier' => 3 + ],*/ + ], + ], + + 'payment' => [ + /* + |-------------------------------------------------------------------------- + | WeChat Payment 配置 + |-------------------------------------------------------------------------- + | + | 微信支付对应配置数据 + | 详见:https://easywechat.com/6.x/pay/index.html + | + */ + // V2 + 'app_id' => env('WECHAT_PAYMENT_SP_APP_ID', ''), + 'sub_app_id' => env('WECHAT_PAYMENT_SUB_APP_ID', ''), + 'mch_id' => env('WECHAT_PAYMENT_SP_MCH_ID', ''), + 'sub_mch_id' => env('WECHAT_PAYMENT_SUB_MCH_ID', ''), + 'private_key' => env('WECHAT_PAYMENT_V2_PRIVATE_KEY_PATH', ''), + 'certificate' => env('WECHAT_PAYMENT_V2_CERTIFICATE_PATH', ''), + 'v2_secret_key' => env('WECHAT_PAYMENT_V2_SECRET_KEY', ''), + + + // V3 + /* + 'sp_app_id' => env('WECHAT_PAYMENT_SP_APP_ID', ''), + 'sub_app_id' => env('WECHAT_PAYMENT_SUB_APP_ID', ''), + 'sp_mch_id' => env('WECHAT_PAYMENT_SP_MCH_ID', ''), + 'sub_mch_id' => env('WECHAT_PAYMENT_SUB_MCH_ID', ''), + 'secret_key' => env('WECHAT_PAYMENT_V3_SECRET_KEY', ''), + 'platform_certs' => [ + // 请使用绝对路径 + // '/path/to/wechatpay/cert.pem', + env('WECHAT_PAYMENT_V3_PRIVATE_KEY_PATH'), + ], + */ + + /* + |-------------------------------------------------------------------------- + | Http 配置 + |-------------------------------------------------------------------------- + | + | 接口请求相关配置,超时时间等,具体可用参数请参考: + | https://github.com/symfony/symfony/blob/5.3/src/Symfony/Contracts/HttpClient/HttpClientInterface.php + | + */ + 'http' => [ + 'throw' => true, // 状态码非 200、300 时是否抛出异常,默认为开启 + 'timeout' => 5.0, + // 'base_uri' => 'https://api.mch.weixin.qq.com/', // 如果你在国外想要覆盖默认的 url 的时候才使用,根据不同的模块配置不同的 uri + ], + ] +]; diff --git a/database/.gitignore b/database/.gitignore new file mode 100644 index 0000000..9b19b93 --- /dev/null +++ b/database/.gitignore @@ -0,0 +1 @@ +*.sqlite* diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php new file mode 100644 index 0000000..584104c --- /dev/null +++ b/database/factories/UserFactory.php @@ -0,0 +1,44 @@ + + */ +class UserFactory extends Factory +{ + /** + * The current password being used by the factory. + */ + protected static ?string $password; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => fake()->name(), + 'email' => fake()->unique()->safeEmail(), + 'email_verified_at' => now(), + 'password' => static::$password ??= Hash::make('password'), + 'remember_token' => Str::random(10), + ]; + } + + /** + * Indicate that the model's email address should be unverified. + */ + public function unverified(): static + { + return $this->state(fn (array $attributes) => [ + 'email_verified_at' => null, + ]); + } +} diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php new file mode 100644 index 0000000..05fb5d9 --- /dev/null +++ b/database/migrations/0001_01_01_000000_create_users_table.php @@ -0,0 +1,49 @@ +id(); + $table->string('name'); + $table->string('email')->unique(); + $table->timestamp('email_verified_at')->nullable(); + $table->string('password'); + $table->rememberToken(); + $table->timestamps(); + }); + + Schema::create('password_reset_tokens', function (Blueprint $table) { + $table->string('email')->primary(); + $table->string('token'); + $table->timestamp('created_at')->nullable(); + }); + + Schema::create('sessions', function (Blueprint $table) { + $table->string('id')->primary(); + $table->foreignId('user_id')->nullable()->index(); + $table->string('ip_address', 45)->nullable(); + $table->text('user_agent')->nullable(); + $table->longText('payload'); + $table->integer('last_activity')->index(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('users'); + Schema::dropIfExists('password_reset_tokens'); + Schema::dropIfExists('sessions'); + } +}; diff --git a/database/migrations/0001_01_01_000001_create_cache_table.php b/database/migrations/0001_01_01_000001_create_cache_table.php new file mode 100644 index 0000000..b9c106b --- /dev/null +++ b/database/migrations/0001_01_01_000001_create_cache_table.php @@ -0,0 +1,35 @@ +string('key')->primary(); + $table->mediumText('value'); + $table->integer('expiration'); + }); + + Schema::create('cache_locks', function (Blueprint $table) { + $table->string('key')->primary(); + $table->string('owner'); + $table->integer('expiration'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('cache'); + Schema::dropIfExists('cache_locks'); + } +}; diff --git a/database/migrations/0001_01_01_000002_create_jobs_table.php b/database/migrations/0001_01_01_000002_create_jobs_table.php new file mode 100644 index 0000000..425e705 --- /dev/null +++ b/database/migrations/0001_01_01_000002_create_jobs_table.php @@ -0,0 +1,57 @@ +id(); + $table->string('queue')->index(); + $table->longText('payload'); + $table->unsignedTinyInteger('attempts'); + $table->unsignedInteger('reserved_at')->nullable(); + $table->unsignedInteger('available_at'); + $table->unsignedInteger('created_at'); + }); + + Schema::create('job_batches', function (Blueprint $table) { + $table->string('id')->primary(); + $table->string('name'); + $table->integer('total_jobs'); + $table->integer('pending_jobs'); + $table->integer('failed_jobs'); + $table->longText('failed_job_ids'); + $table->mediumText('options')->nullable(); + $table->integer('cancelled_at')->nullable(); + $table->integer('created_at'); + $table->integer('finished_at')->nullable(); + }); + + Schema::create('failed_jobs', function (Blueprint $table) { + $table->id(); + $table->string('uuid')->unique(); + $table->text('connection'); + $table->text('queue'); + $table->longText('payload'); + $table->longText('exception'); + $table->timestamp('failed_at')->useCurrent(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('jobs'); + Schema::dropIfExists('job_batches'); + Schema::dropIfExists('failed_jobs'); + } +}; diff --git a/database/migrations/2023_03_03_075720_create_patients_table.php b/database/migrations/2023_03_03_075720_create_patients_table.php new file mode 100644 index 0000000..90466ab --- /dev/null +++ b/database/migrations/2023_03_03_075720_create_patients_table.php @@ -0,0 +1,42 @@ +id(); + $table->string('union_id', 32)->default('')->comment(' union_id'); + $table->string('open_id', 32)->index()->comment('openid'); + $table->string('patient_id', 20)->index()->comment('患者门诊号'); + $table->unsignedTinyInteger('card_type')->index()->comment('患者证件类型'); + $table->string('card_no', 64)->index()->comment('患者证件号'); + $table->string('name', 50)->comment('患者名称'); + $table->unsignedTinyInteger('sex')->comment('性别 1男 2女'); + $table->string('birthday', 10)->comment('出生日期'); + $table->string('mobile_phone', 64)->default('')->comment('手机号码'); + $table->string('address', 255)->default('')->comment('建档地址'); + $table->unsignedTinyInteger('def_status')->index()->default(0)->comment('默认选择 0否 1是'); + $table->string('qr_code_text')->default('')->comment('二维码文本'); + $table->string('health_card_id')->default('')->comment('健康卡ID'); + $table->unsignedTinyInteger('health_card_status')->default(0)->comment('健康卡状态'); + $table->timestamps(); + $table->engine = 'MyISAM'; + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('patients'); + } +}; diff --git a/database/migrations/2023_03_03_080312_create_orders_table.php b/database/migrations/2023_03_03_080312_create_orders_table.php new file mode 100644 index 0000000..68f2e76 --- /dev/null +++ b/database/migrations/2023_03_03_080312_create_orders_table.php @@ -0,0 +1,47 @@ +id(); + $table->integer('relate_id')->default(0)->comment('关联订单号'); + $table->string('order_id', 64)->unique()->comment('订单ID'); + $table->string('his_order_id', 64)->comment('his订单ID'); + $table->string('transaction_id', 64)->comment('交易平台单号'); + $table->unsignedTinyInteger('status')->index()->comment('订单状态 0未执行 1异常 2成功 3冲正 4失败'); + $table->unsignedTinyInteger('notify_status')->index()->comment('回调消息状态 0未收到 1已收到回调'); + $table->unsignedTinyInteger('type')->index()->comment('订单类型 1当天挂号 2预约挂号 3门诊缴费 4住院按金 5门诊预交金充值 6住院预交金充值 7门诊预交金退费 8住院预交金退费'); + $table->unsignedTinyInteger('pay_type')->index()->comment('支付类型 1聚合 2银联 3微信 4支付宝 5医保支付'); + $table->unsignedTinyInteger('pay_mode')->index()->comment('支付模式 1支付/充值 2退款'); + $table->integer('fee')->comment('总交易金额 分'); + $table->integer('reduce_fee')->comment('减免金额 分'); + $table->integer('self_fee')->comment('自费金额 分'); + $table->integer('refund_fee')->comment('累计退费金额 分'); + $table->string('open_id', 32)->index()->comment('openid'); + $table->string('patient_id', 20)->index()->comment('患者ID'); + $table->string('patient_name', 50)->comment('患者名称'); + $table->unsignedTinyInteger('source_id')->index()->comment('订单来源 1微信公众号 2微信小程序'); + $table->timestamp('payment_at')->nullable()->comment('支付时间'); + $table->timestamp('refunded_at')->nullable()->comment('退费时间'); + $table->timestamps(); + $table->engine = 'InnoDB'; + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('orders'); + } +}; diff --git a/database/migrations/2023_03_03_080715_create_registration_records_table.php b/database/migrations/2023_03_03_080715_create_registration_records_table.php new file mode 100644 index 0000000..68d4023 --- /dev/null +++ b/database/migrations/2023_03_03_080715_create_registration_records_table.php @@ -0,0 +1,43 @@ +id(); + $table->integer('relate_order_id')->index()->comment('关联订单表ID'); + $table->integer('relate_patient_id')->index()->comment('关联患者表ID'); + $table->string('reg_id', 20)->index()->comment('挂号流水号'); + $table->string('dept_id', 10)->comment('科室ID'); + $table->string('dept_name', 20)->comment('科室名称'); + $table->string('dept_location', 50)->comment('科室地址'); + $table->string('doctor_id', 20)->comment('医生ID'); + $table->string('doctor_name', 20)->comment('医生名称'); + $table->string('visit_date', 10)->index()->comment('就诊日期'); + $table->string('begin_time', 5)->index()->comment('开始时间'); + $table->string('end_time', 5)->comment('结束时间'); + $table->tinyInteger('lock_status')->index()->comment('锁号状态 1 已锁号 2 已解锁'); + $table->timestamp('lock_at')->nullable()->comment('锁号时间'); + $table->timestamp('unlock_at')->nullable()->comment('解锁时间'); + $table->text('extra_info')->comment('额外的挂号信息'); + $table->timestamps(); + $table->engine = 'InnoDB'; + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('registration_records'); + } +}; diff --git a/database/migrations/2023_03_03_080952_create_outpatient_payment_records_table.php b/database/migrations/2023_03_03_080952_create_outpatient_payment_records_table.php new file mode 100644 index 0000000..ba8afae --- /dev/null +++ b/database/migrations/2023_03_03_080952_create_outpatient_payment_records_table.php @@ -0,0 +1,40 @@ +id(); + $table->integer('relate_order_id')->index()->comment('关联订单表ID'); + $table->integer('relate_patient_id')->index()->comment('关联患者表ID'); + $table->string('dept_id', 10)->comment('科室ID'); + $table->string('dept_name', 20)->comment('科室名称'); + $table->string('doctor_id', 20)->comment('医生ID'); + $table->string('doctor_name', 20)->comment('医生名称'); + $table->string('visit_date', 10)->index()->comment('就诊日期'); + $table->integer('total_amount')->comment('处方总金额'); + $table->tinyInteger('pre_settle_status')->index()->comment('预结算状态 1 已预结算 2 已取消预结算'); + $table->timestamp('pre_settle_at')->nullable()->comment('预结算时间'); + $table->timestamp('cancel_pre_settle_at')->nullable()->comment('取消预结算时间'); + $table->text('extra_info')->comment('额外的门诊信息'); + $table->timestamps(); + $table->engine = 'InnoDB'; + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('outpatient_payment_records'); + } +}; diff --git a/database/migrations/2023_03_03_082229_create_push_wechat_messages_table.php b/database/migrations/2023_03_03_082229_create_push_wechat_messages_table.php new file mode 100644 index 0000000..a96c222 --- /dev/null +++ b/database/migrations/2023_03_03_082229_create_push_wechat_messages_table.php @@ -0,0 +1,38 @@ +id(); + $table->integer('relate_order_id')->default(0)->index()->comment('关联订单表ID'); + $table->integer('relate_patient_id')->index()->comment('关联患者表ID'); + $table->tinyInteger('type')->index()->comment('消息类型 1公众号模板消息 2公众号订阅消息 3公众号一次性订阅消息 4公众号客服消息'); + $table->string('template_id', 64)->comment('推送消息ID'); + $table->string('scene', 32)->default('')->comment('订阅场景'); + $table->text('content')->comment('序列化推送消息内容'); + $table->unsignedTinyInteger('number')->default(0)->index()->comment('已尝试发送次数'); + $table->unsignedTinyInteger('status')->default(0)->index()->comment('状态 0尚未推送 1已推送 2推送异常'); + $table->string('msg_id', 32)->default('')->comment('推送后返回的msg_id'); + $table->timestamp('sent_at')->nullable()->comment('推送时间'); + $table->timestamps(); + $table->engine = 'InnoDB'; + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('push_wechat_messages'); + } +}; diff --git a/database/migrations/2023_03_03_090721_create_departments_table.php b/database/migrations/2023_03_03_090721_create_departments_table.php new file mode 100644 index 0000000..82af503 --- /dev/null +++ b/database/migrations/2023_03_03_090721_create_departments_table.php @@ -0,0 +1,36 @@ +bigIncrements('id'); + $table->integer('p_id')->default(0)->comment('父类ID代码'); + $table->string('dept_id', 10)->default('')->comment('科室代码')->index(); + $table->string('dept_name', 50)->comment('科室名称'); + $table->integer('sort')->default(0)->comment('排序 正向排序'); + $table->tinyInteger('is_enable')->default(1)->comment('是否启用 0 不启用 1 启用'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('departments'); + } +} diff --git a/database/migrations/2023_03_03_090722_create_department_tips_table.php b/database/migrations/2023_03_03_090722_create_department_tips_table.php new file mode 100644 index 0000000..44b0da0 --- /dev/null +++ b/database/migrations/2023_03_03_090722_create_department_tips_table.php @@ -0,0 +1,35 @@ +bigIncrements('id'); + $table->string('dept_id', 10)->comment('科室ID'); + $table->string('title', 50)->comment('提示标题'); + $table->text('message')->comment('提示内容'); + $table->tinyInteger('is_enable')->default(1)->comment('是否启用 0 否 1 是'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('department_tips'); + } +} diff --git a/database/migrations/2023_03_03_090728_create_doctors_table.php b/database/migrations/2023_03_03_090728_create_doctors_table.php new file mode 100644 index 0000000..537a415 --- /dev/null +++ b/database/migrations/2023_03_03_090728_create_doctors_table.php @@ -0,0 +1,37 @@ +id(); + $table->string('doctor_id', 10)->index()->comment('医生ID'); + $table->string('doctor_name', 10)->comment('医生名称'); + $table->string('dept_id', 10)->index()->comment('科室ID'); + $table->string('dept_name', 20)->comment('医生名称'); + $table->string('title', 20)->comment('职称'); + $table->string('image', 128)->comment('医生图片'); + $table->smallInteger('sort')->index()->default(0)->comment('排序'); + $table->text('adept')->comment('擅长简介'); + $table->text('intro')->comment('详情'); + $table->timestamps(); + $table->engine = 'MyISAM'; + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('doctors'); + } +}; diff --git a/database/migrations/2023_03_03_094331_create_inpatients_table.php b/database/migrations/2023_03_03_094331_create_inpatients_table.php new file mode 100644 index 0000000..6c7b569 --- /dev/null +++ b/database/migrations/2023_03_03_094331_create_inpatients_table.php @@ -0,0 +1,32 @@ +id(); + $table->string('open_id', 32)->index()->comment('open_id'); + $table->string('inpatient_id', 32)->index()->comment('住院号'); + $table->string('inpatient_name', 50)->comment('患者名称'); + $table->timestamp('in_time')->comment('入院时间'); + $table->timestamps(); + $table->engine = 'MyISAM'; + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('inpatients'); + } +}; diff --git a/database/migrations/2023_03_06_011612_create_inpatient_recharge_records_table.php b/database/migrations/2023_03_06_011612_create_inpatient_recharge_records_table.php new file mode 100644 index 0000000..5e2b780 --- /dev/null +++ b/database/migrations/2023_03_06_011612_create_inpatient_recharge_records_table.php @@ -0,0 +1,36 @@ +id(); + $table->integer('relate_id')->index()->comment('关联住院表ID'); + $table->integer('relate_order_id')->index()->comment('关联订单表ID'); + $table->string('dept_id', 10)->comment('科室ID'); + $table->string('dept_name', 20)->comment('科室名称'); + $table->string('doctor_id', 20)->comment('医生ID'); + $table->string('doctor_name', 20)->comment('医生名称'); + $table->integer('recharge_amount')->comment('充值金额'); + $table->text('extra_info')->comment('额外的就医信息'); + $table->timestamps(); + $table->engine = 'InnoDB'; + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('inpatient_recharge_records'); + } +}; diff --git a/database/migrations/2023_03_06_013450_create_health_card_reports_table.php b/database/migrations/2023_03_06_013450_create_health_card_reports_table.php new file mode 100644 index 0000000..77564ab --- /dev/null +++ b/database/migrations/2023_03_06_013450_create_health_card_reports_table.php @@ -0,0 +1,35 @@ +id(); + $table->integer('relate_order_id')->index()->comment('关联订单表ID'); + $table->integer('relate_patient_id')->index()->comment('关联患者表ID'); + $table->string('scene', 10)->index()->comment('用卡环节代码'); + $table->text('report_data')->comment('健康卡上报内容'); + $table->unsignedTinyInteger('number')->default(0)->comment('已尝试发送次数'); + $table->unsignedTinyInteger('status')->default(0)->index()->comment('状态 0尚未推送 1已上报'); + $table->timestamp('report_at')->nullable()->comment('推送时间'); + $table->timestamps(); + $table->engine = 'InnoDB'; + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('health_card_reports'); + } +}; diff --git a/database/migrations/2024_12_17_083924_create_personal_access_tokens_table.php b/database/migrations/2024_12_17_083924_create_personal_access_tokens_table.php new file mode 100644 index 0000000..e828ad8 --- /dev/null +++ b/database/migrations/2024_12_17_083924_create_personal_access_tokens_table.php @@ -0,0 +1,33 @@ +id(); + $table->morphs('tokenable'); + $table->string('name'); + $table->string('token', 64)->unique(); + $table->text('abilities')->nullable(); + $table->timestamp('last_used_at')->nullable(); + $table->timestamp('expires_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('personal_access_tokens'); + } +}; diff --git a/database/migrations/2024_12_24_110504_create_personal_access_tokens_table.php b/database/migrations/2024_12_24_110504_create_personal_access_tokens_table.php new file mode 100644 index 0000000..e828ad8 --- /dev/null +++ b/database/migrations/2024_12_24_110504_create_personal_access_tokens_table.php @@ -0,0 +1,33 @@ +id(); + $table->morphs('tokenable'); + $table->string('name'); + $table->string('token', 64)->unique(); + $table->text('abilities')->nullable(); + $table->timestamp('last_used_at')->nullable(); + $table->timestamp('expires_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('personal_access_tokens'); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php new file mode 100644 index 0000000..d01a0ef --- /dev/null +++ b/database/seeders/DatabaseSeeder.php @@ -0,0 +1,23 @@ +create(); + + User::factory()->create([ + 'name' => 'Test User', + 'email' => 'test@example.com', + ]); + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..0d10472 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "private": true, + "type": "module", + "scripts": { + "build": "vite build", + "dev": "vite" + }, + "devDependencies": { + "autoprefixer": "^10.4.20", + "axios": "^1.7.4", + "concurrently": "^9.0.1", + "laravel-vite-plugin": "^1.0", + "postcss": "^8.4.47", + "tailwindcss": "^3.4.13", + "vite": "^6.0" + } +} diff --git a/packagist/unify_payment/.gitignore b/packagist/unify_payment/.gitignore new file mode 100644 index 0000000..8b3c249 --- /dev/null +++ b/packagist/unify_payment/.gitignore @@ -0,0 +1,4 @@ +/vendor/ +.idea/ +composer.lock +*.log \ No newline at end of file diff --git a/packagist/unify_payment/README.md b/packagist/unify_payment/README.md new file mode 100644 index 0000000..8c92456 --- /dev/null +++ b/packagist/unify_payment/README.md @@ -0,0 +1,97 @@ +# Unify-Payment + +方鼎统一支付平台v2,PHP接入版 + +## 使用 + +php环境需 >= 7.4 + +### Composer 安装 + +1. composer.json 的 repositories 项添加本地文件夹路径 +```json +{ + "repositories": [ + { + "type": "path", + "url": "./unify_payment" + } + ] +} +``` + +2. 执行require命令 + +```bash +$ composer require fd_wechat/unify_payment +``` + +## 基本用法 + +### 支付模块 + +#### miniClient / officialClient +```php +mini->jsapi($order); + +// 公众号支付 +Factory::pay($config)->official->jsapi($order); + +``` + +### 通用模块 + +#### orderClient +```php +order->query($order); + +// 订单关闭 +$order = new CloseOrder('FD2022080310000055123451223'); +Factory::common($config)->order->close($order); + +// 订单退款 +$order = new RefundOrder('FD2022080310000055123451223', 'FD2022080310000055123451223_12345', 0.01, 'Test Refund'); +Factory::common($config)->order->refund($order); + +// 外部订单确认 +$order = new ConfirmOrderForEx('FD2022080310000055123451223', 'ZSD0002974832', 'ow8NuxN_ALO2MzSvzj19hyxWjCVY', '4200067674202302063709953797'); +Factory::common($config)->order->confirmForEx($order); + +``` + +### 添加第三方Client +```php +$app = Factory::pay($config); +$app->registerOtherClient('other', OtherClient::class); +$app->other->doSomeThing(...); +``` + +### 数据Mock + +Mock数据类在目录 `./src/Mock` 下 + +```php + +$mockHandler = new CreateWeChatOrderHandler(true); +Factory::pay($config)->official->setMockHandler([$mockHandler])->jsapi($order); +``` \ No newline at end of file diff --git a/packagist/unify_payment/composer.json b/packagist/unify_payment/composer.json new file mode 100644 index 0000000..3183ef2 --- /dev/null +++ b/packagist/unify_payment/composer.json @@ -0,0 +1,48 @@ +{ + "name": "fd_wechat/unify_payment", + "description": "Access to FangDing unified payment platform.", + "type": "library", + "license": "MIT", + "require": { + "php": ">=8.0", + "ext-json": "*", + "ext-http": "*", + "ext-libxml": "*", + "ext-simplexml": "*", + "laravel/framework": "^9.0|^10.0|^11.0", + "guzzlehttp/guzzle": "~6.0", + "monolog/monolog": "^2.0", + "pimple/pimple": "^3.0" + }, + "require-dev": { + "fakerphp/faker": "~1.17.0", + "phpunit/phpunit": "^8.5.14" + }, + "autoload": { + "psr-4": { + "UnifyPayment\\": "src/" + } + }, + "autoload-dev": { + "classmap": [ + "tests/" + ] + }, + "extra": { + "laravel": { + "providers": [ + "UnifyPayment\\UnifyServiceProvider" + ] + }, + "aliases": { + "Unify": "UnifyPayment\\Unify" + } + }, + "authors": [ + { + "name": "XieJC", + "email": "rmiku@qq.com" + } + ], + "minimum-stability": "dev" +} diff --git a/packagist/unify_payment/config/unify.php b/packagist/unify_payment/config/unify.php new file mode 100644 index 0000000..31b8640 --- /dev/null +++ b/packagist/unify_payment/config/unify.php @@ -0,0 +1,113 @@ + 'http://fangding.picp.vip:5025', + + 'apiSecret' => '123456', + + /* + |-------------------------------------------------------------------------- + | Payment Channels Config + |-------------------------------------------------------------------------- + | + | 统一支付平台支付渠道配置参数 + | merchantId: 商户号 + | secret: 秘钥 + | channelId: 支付渠道 + | operatorId: [ 操作员1 , 操作员2 ] + | + */ + + 'channels' => [ + + 'default' => [ + 'merchantId' => 'QY2Y20230301', + 'secret' => 'af4d32227ed4d63e1bb8726ebd0b6110', + 'channelId' => 'FD001', + 'operatorId' => [ + 'wx001', + ], + ] + + ], + + /* + |-------------------------------------------------------------------------- + | Logger Config + |-------------------------------------------------------------------------- + | + | 详细使用方式见 MonoLog 文档:https://github.com/Seldaek/monolog/blob/2.x/README.md + | format: + | LineFormatter 字符串 + | JsonFormatter Json + | ... + | handler: + | StreamHandler 单个文件 + | RotatingFileHandler 每日记录一次 + | RedisHandler Redis + | ... + | handler_with: + | RotatingFileHandler: + | file 文件名称 + | max_files 最大记录日志文件数量,0为无限制 + | level 日志记录等级 + | OtherHandler详见MonoLog文档 + | ... + | + */ + + 'log' => [ + // 时区 + 'timezone' => 'Asia/Shanghai', + // 日期格式 + 'date_format' => 'Y-m-d H:i:s', + // 格式化器 + 'formatter' => LineFormatter::class, + // 对应格式化器构造参数 + 'formatter_with' => [ + // 输出格式字符串 + 'output' => "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n\n" + ], + // 处理器 + 'handler' => RotatingFileHandler::class, + // 对应处理器构造参数 + 'handler_with' => [ + // 文件 + 'file' => __DIR__ . '/../logs/my_app.log', + // 最大文件 0 无限制 + 'max_files' => 0, + // 记录等级 + 'level' => Logger::DEBUG + ] + ], + + /* + |-------------------------------------------------------------------------- + | Http Client Config + |-------------------------------------------------------------------------- + | + | 详细使用方式见 Guzzle 文档:https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html + | + */ + 'http' => [ + 'verify' => false, + 'timeout' => 20, + 'connect_timeout' => 5.0, + ] +]; + diff --git a/packagist/unify_payment/phpunit.xml b/packagist/unify_payment/phpunit.xml new file mode 100644 index 0000000..8a2317a --- /dev/null +++ b/packagist/unify_payment/phpunit.xml @@ -0,0 +1,18 @@ + + + + + ./tests/CommonTest.php + ./tests/PayTest.php + + + + + + + + diff --git a/packagist/unify_payment/src/Cores/BasicClient.php b/packagist/unify_payment/src/Cores/BasicClient.php new file mode 100644 index 0000000..843337f --- /dev/null +++ b/packagist/unify_payment/src/Cores/BasicClient.php @@ -0,0 +1,286 @@ +config = is_array($config) ? new Config($config) : $config; + $this->domain = $this->config->get('domain', ''); + + $this->setHttpOptions($this->config->get('http', [])); + $this->setChannels(); + } + + /** + * Get Config + * + * @return ConfigInterface + */ + public function getConfig(): ConfigInterface + { + return $this->config; + } + + /** + * Set Config. + * + * @param ConfigInterface $config + * + * @return $this + */ + public function setConfig(ConfigInterface $config): self + { + $this->config = $config; + + return $this; + } + + /** + * Get Channels + * + * @return Channels + */ + protected function getChannels(): Channels + { + return $this->channels; + } + + /** + * Set payment channels. + * + * @return void + * + * @throws ReflectionException + * @throws InvalidConfigException + */ + protected function setChannels(): void + { + $channels = $this->config->get('channels'); + if (!array_key_exists($this->channel, $channels)) { + throw new InvalidConfigException('Missing Unify Payment Config -- [channels]'); + } + + $channels = $channels[$this->channel]; + + $this->channels = new Channels(); + $this->channels->setMerchantId($channels['merchantId']); + $this->channels->setSecret($channels['secret']); + $this->channels->setOperatorId(reset($channels['operatorId'])); + $this->channels->setChannelId($channels['channelId']); + + $this->payload = array_merge($this->channels->toArray(), $this->payload); + unset($this->payload['secret']); + } + + /** + * Get Operator ID + * + * @return string + */ + public function getChannelsOperatorId(): string + { + return $this->getChannels()->getOperatorId(); + } + + /** + * Set Operator ID + * + * @param int $index + * + * @return $this + * + * @throws InvalidConfigException + */ + public function setChannelsOperatorId(int $index = 0): self + { + $channels = $this->config->get('channels'); + if (!array_key_exists($this->channel, $channels)) { + throw new InvalidConfigException('Missing Unify Payment Config -- [channels]'); + } + + $channels = $channels[$this->channel]; + + if (!array_key_exists($index, $channels['operatorId'])) { + throw new InvalidConfigException('Missing Unify Payment Config -- [operatorId]-[index:' . $index . ']'); + } + + $this->channels->setOperatorId($channels['operatorId'][$index]); + $this->payload['operatorId'] = $channels['operatorId'][$index]; + + return $this; + } + + /** + * Set unify payment channel name. + * + * @param string $name + * + * @return BasicClient + * + * @throws ReflectionException|InvalidConfigException + */ + public function setChannel(string $name): BasicClient + { + $this->channel = $name; + $this->setChannels(); + return $this; + } + + /** + * Get unify payment channel name. + * + * @return string + */ + public function getChannel(): string + { + return $this->channel; + } + + /** + * Generate Sign. + * + * @param array $data + * + * @return string + * + * @throws InvalidConfigException + * @throws RuntimeException + */ + final public function generateSign(array $data): string + { + $key = $this->getChannels()->getSecret(); + + if (is_null($key)) { + throw new InvalidConfigException('Missing Unify Payment Config -- [SecretKey]'); + } + + ksort($data); + + return md5($this->getSignContent($data) . $key); + } + + /** + * Generate Sign Content. + * + * @param array $data + * + * @return string + * + * @throws RuntimeException + */ + final protected function getSignContent(array $data): string + { + if (array_key_exists('sign', $data)) { + unset($data['sign']); + } + + try { + $buff = json_encode($data, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE); + } catch (JsonException $e) { + throw new RuntimeException('Generate Sign Error:' . $e->getMessage()); + } + + Log::debug('Generate Sign Content', [$data, $buff]); + + return trim($buff); + } + + /** + * Set http options + * + * @param array $httpOptions + * + * @return void + */ + public function setHttpOptions(array $httpOptions): void + { + $this->httpOptions = array_merge($this->httpOptions, $httpOptions, ['base_uri' => $this->domain]); + } + + /** + * Send json request. + * + * @param string $method + * @param string $endpoint + * @param array $options + * + * @return array|string + */ + public function request(string $method, string $endpoint, array $options = []) + { + if ($method === 'post') { + $options['json'] = $this->payload; + } + + $requestId = ['requestID' => md5(microtime())]; + + Log::debug('Request Content', [$requestId, $method, $endpoint, $options]); + + $response = $this->unwrapResponse($this->getHttpClient()->{$method}($endpoint, $options)); + + Log::debug('Response Content', [$requestId, $response]); + + return $response; + } +} \ No newline at end of file diff --git a/packagist/unify_payment/src/Cores/BasicContainer.php b/packagist/unify_payment/src/Cores/BasicContainer.php new file mode 100644 index 0000000..df77450 --- /dev/null +++ b/packagist/unify_payment/src/Cores/BasicContainer.php @@ -0,0 +1,140 @@ +config = is_array($config) ? new Config($config) : $config; + $this->registerAllClient(); + } + + /** + * Register All Client. + * + * @return void + * + * @throws InvalidClientException + * @throws ClassNotExistsException + */ + private function registerAllClient(): void + { + foreach ($this->clients as $key => $value) { + if (!class_exists($value)) { + throw new ClassNotExistsException('Class [' . $value . '] Not Found.', $value); + } + + if (!is_subclass_of($value, BasicClient::class)) { + throw new InvalidClientException('Invalid Client [' . $value . '].', $value); + } + + $this->container[$key] = new $value($this->config); + } + } + + /** + * Register Other Client. + * + * @param string $name + * @param string $client + * + * @return void + * + * @throws ClassNotExistsException + * @throws InvalidClientException + */ + public function registerOtherClient(string $name, string $client): void + { + if (!class_exists($client)) { + throw new ClassNotExistsException('Class [' . $client . '] Not Found.', $client); + } + + if (!is_subclass_of($client, BasicClient::class)) { + throw new InvalidClientException('Invalid Client [' . $client . '].', $client); + } + + $this->container[$name] = new $client($this->config); + } + + /** + * Get Magic Property. + * + * @param string $name + * + * @return BasicClient + * + * @throws InvalidClientException + */ + public function __get(string $name): BasicClient + { + if (!isset($this->container[$name])) { + throw new InvalidClientException($name); + } + + return $this->container[$name]; + } + + /** + * Set Magic Property. + * + * @param string $name + * @param BasicClient $value + * + * @return void + * + * @throws InvalidClientException + */ + public function __set(string $name, BasicClient $value) + { + if (isset($this->container[$name])) { + throw new InvalidClientException('Override Client [' . $name . '] Not Allowed.', [$name, $value]); + } + + $this->container[$name] = $value; + } + + /** + * Isset Magic Property. + * + * @param string $name + * + * @return bool + */ + public function __isset(string $name) + { + return (bool)$this->container[$name]; + } +} diff --git a/packagist/unify_payment/src/Cores/Config.php b/packagist/unify_payment/src/Cores/Config.php new file mode 100644 index 0000000..0e79328 --- /dev/null +++ b/packagist/unify_payment/src/Cores/Config.php @@ -0,0 +1,173 @@ + $value) { + $this->set($key, $value); + } + } + + /** + * + * @param string|array $key + * @param mixed $default + * + * @return mixed + */ + public function get($key, $default = null) + { + if (is_array($key)) { + return $this->getMany($key); + } + + return Arr::get($this->items, $key, $default); + } + + /** + * + * @param array $keys + * + * @return array + */ + public function getMany(array $keys): array + { + $config = []; + + foreach ($keys as $key => $default) { + if (is_numeric($key)) { + [$key, $default] = [$default, null]; + } + + $config[$key] = Arr::get($this->items, $key, $default); + } + + return $config; + } + + /** + * + * @param string $key + * @param mixed $value + * + * @return void + */ + public function set(string $key, $value = null): void + { + Arr::set($this->items, $key, $value); + } + + /** + * + * @param string $key + * + * @return bool + */ + public function has(string $key): bool + { + return Arr::has($this->items, $key); + } + + /** + * @param string $key + * + * @return void + */ + public function forget(string $key): void + { + Arr::forget($this->items, $key); + } + + /** + * @return array + */ + public function all(): array + { + return $this->items; + } + + /** + * build to array + * + * @return array + */ + public function toArray(): array + { + return $this->all(); + } + + /** + * build to json + * + * @param int $option + * + * @return string + * + * @throws \JsonException + */ + public function toJson(int $option = JSON_UNESCAPED_UNICODE): string + { + return json_encode($this->all(), JSON_THROW_ON_ERROR | $option); + } + + public function __get(string $key) + { + return $this->get($key); + } + + public function __set(string $key, $value) + { + $this->set($key, $value); + } + + public function __isset(string $key): bool + { + return $this->has($key); + } + + public function __unset(string $key) + { + $this->forget($key); + } + + public function offsetExists($offset): bool + { + return $this->has((string)$offset); + } + + public function offsetGet($offset): mixed + { + return $this->get((string)$offset); + } + + public function offsetSet(mixed $offset, $value):void + { + $this->set((string)$offset, $value); + } + + public function offsetUnset($offset): void + { + $this->set((string)$offset, null); + } +} \ No newline at end of file diff --git a/packagist/unify_payment/src/Cores/Contracts/Config.php b/packagist/unify_payment/src/Cores/Contracts/Config.php new file mode 100644 index 0000000..a6699d1 --- /dev/null +++ b/packagist/unify_payment/src/Cores/Contracts/Config.php @@ -0,0 +1,41 @@ +raw = is_array($raw) ? $raw : [$raw]; + + parent::__construct($message); + } +} diff --git a/packagist/unify_payment/src/Cores/Exceptions/InvalidClientException.php b/packagist/unify_payment/src/Cores/Exceptions/InvalidClientException.php new file mode 100644 index 0000000..14b99ba --- /dev/null +++ b/packagist/unify_payment/src/Cores/Exceptions/InvalidClientException.php @@ -0,0 +1,19 @@ +setMerchantId($merchantId) + ->setSecret($secret) + ->setOperatorId($operatorId) + ->setChannelId($channelId); + } + + /** + * Get merchantId value + * + * @return string|null + */ + public function getMerchantId(): string + { + return $this->merchantId; + } + + /** + * Set merchantId value + * + * @param string $merchantId + * + * @return Channels + */ + public function setMerchantId(string $merchantId = ''): Channels + { + // validation for constraint: string + if (!is_string($merchantId)) { + throw new InvalidArgumentException( + sprintf( + 'Invalid value %s, please provide a string, %s given', + var_export($merchantId, true), + gettype($merchantId) + ), __LINE__ + ); + } + + $this->merchantId = $merchantId; + return $this; + } + + /** + * Get secret value + * + * @return string + */ + public function getSecret(): ?string + { + return $this->secret; + } + + /** + * Set secret value + * + * @param string $secret + * + * @return Channels + */ + public function setSecret(string $secret = ''): Channels + { + // validation for constraint: string + if (!is_string($secret)) { + throw new InvalidArgumentException( + sprintf( + 'Invalid value %s, please provide a string, %s given', + var_export($secret, true), + gettype($secret) + ), __LINE__ + ); + } + + $this->secret = $secret; + return $this; + } + + /** + * Get operatorId value + * + * @return string + */ + public function getOperatorId(): ?string + { + return $this->operatorId; + } + + /** + * Set operatorId value + * + * @param string $operatorId + * + * @return Channels + */ + public function setOperatorId(string $operatorId = ''): Channels + { + // validation for constraint: string + if (!is_string($operatorId)) { + throw new InvalidArgumentException( + sprintf( + 'Invalid value %s, please provide a string, %s given', + var_export($operatorId, true), + gettype($operatorId) + ), __LINE__ + ); + } + + $this->operatorId = $operatorId; + return $this; + } + + /** + * Get channelId value + * + * @return string + */ + public function getChannelId(): string + { + return $this->channelId; + } + + /** + * Set channelId value + * + * @param string $channelId + * + * @return Channels + */ + public function setChannelId(string $channelId = ''): Channels + { + // validation for constraint: string + if (!is_string($channelId)) { + throw new InvalidArgumentException( + sprintf( + 'Invalid value %s, please provide a string, %s given', + var_export($channelId, true), + gettype($channelId) + ), __LINE__ + ); + } + + $this->channelId = $channelId; + return $this; + } +} \ No newline at end of file diff --git a/packagist/unify_payment/src/Cores/Struct/CloseOrder.php b/packagist/unify_payment/src/Cores/Struct/CloseOrder.php new file mode 100644 index 0000000..7533d5b --- /dev/null +++ b/packagist/unify_payment/src/Cores/Struct/CloseOrder.php @@ -0,0 +1,65 @@ +setOrderNo($orderNo); + } + + /** + * Get orderNo value + * + * @return string|null + */ + public function getOrderNo(): string + { + return $this->orderNo; + } + + /** + * Set orderNo value + * + * @param string $orderNo + * + * @return CloseOrder + */ + public function setOrderNo(string $orderNo = ''): CloseOrder + { + // validation for constraint: string + if (!is_string($orderNo)) { + throw new InvalidArgumentException( + sprintf( + 'Invalid value %s, please provide a string, %s given', + var_export($orderNo, true), + gettype($orderNo) + ), __LINE__ + ); + } + + $this->orderNo = $orderNo; + return $this; + } +} \ No newline at end of file diff --git a/packagist/unify_payment/src/Cores/Struct/ConfirmOrderForEx.php b/packagist/unify_payment/src/Cores/Struct/ConfirmOrderForEx.php new file mode 100644 index 0000000..d58747b --- /dev/null +++ b/packagist/unify_payment/src/Cores/Struct/ConfirmOrderForEx.php @@ -0,0 +1,190 @@ +setOrderNo($orderNo) + ->setHisSerialNo($hisSerialNo) + ->setPayAccount($payAccount) + ->setTransNo($transNo); + } + + /** + * Get orderNo value + * + * @return string|null + */ + public function getOrderNo(): string + { + return $this->orderNo; + } + + /** + * Set orderNo value + * + * @param string $orderNo + * + * @return ConfirmOrderForEx + */ + public function setOrderNo(string $orderNo = ''): ConfirmOrderForEx + { + // validation for constraint: string + if (!is_string($orderNo)) { + throw new InvalidArgumentException( + sprintf( + 'Invalid value %s, please provide a string, %s given', + var_export($orderNo, true), + gettype($orderNo) + ), __LINE__ + ); + } + + $this->orderNo = $orderNo; + return $this; + } + + /** + * Get hisSerialNo value + * + * @return string|null + */ + public function getHisSerialNo(): string + { + return $this->hisSerialNo; + } + + /** + * Set hisSerialNo value + * + * @param string $hisSerialNo + * + * @return ConfirmOrderForEx + */ + public function setHisSerialNo(string $hisSerialNo = ''): ConfirmOrderForEx + { + // validation for constraint: string + if (!is_string($hisSerialNo)) { + throw new InvalidArgumentException( + sprintf( + 'Invalid value %s, please provide a string, %s given', + var_export($hisSerialNo, true), + gettype($hisSerialNo) + ), __LINE__ + ); + } + + $this->hisSerialNo = $hisSerialNo; + return $this; + } + + /** + * Get payAccount value + * + * @return string|null + */ + public function getPayAccount(): string + { + return $this->payAccount; + } + + /** + * Set payAccount value + * + * @param string $payAccount + * + * @return ConfirmOrderForEx + */ + public function setPayAccount(string $payAccount = ''): ConfirmOrderForEx + { + // validation for constraint: string + if (!is_string($payAccount)) { + throw new InvalidArgumentException( + sprintf( + 'Invalid value %s, please provide a string, %s given', + var_export($payAccount, true), + gettype($payAccount) + ), __LINE__ + ); + } + + $this->payAccount = $payAccount; + return $this; + } + + /** + * Get transNo value + * + * @return string|null + */ + public function getTransNo(): string + { + return $this->transNo; + } + + /** + * Set transNo value + * + * @param string $transNo + * + * @return ConfirmOrderForEx + */ + public function setTransNo(string $transNo = ''): ConfirmOrderForEx + { + // validation for constraint: string + if (!is_string($transNo)) { + throw new InvalidArgumentException( + sprintf( + 'Invalid value %s, please provide a string, %s given', + var_export($transNo, true), + gettype($transNo) + ), __LINE__ + ); + } + + $this->transNo = $transNo; + return $this; + } +} \ No newline at end of file diff --git a/packagist/unify_payment/src/Cores/Struct/CreateOrder.php b/packagist/unify_payment/src/Cores/Struct/CreateOrder.php new file mode 100644 index 0000000..122089d --- /dev/null +++ b/packagist/unify_payment/src/Cores/Struct/CreateOrder.php @@ -0,0 +1,355 @@ +setTitle($title) + ->setOrderNo($orderNo) + ->setAmount($amount) + ->setTag($tag) + ->setAttach($attach) + ->setOrderType($orderType) + ->setOpenId($openId) + ->setNotifyUrl($notifyUrl); + } + + /** + * Get title value + * + * @return string|null + */ + public function getTitle(): string + { + return $this->title; + } + + /** + * Set title value + * + * @param string $title + * + * @return CreateOrder + */ + public function setTitle(string $title = ''): CreateOrder + { + // validation for constraint: string + if (!is_string($title)) { + throw new InvalidArgumentException( + sprintf( + 'Invalid value %s, please provide a string, %s given', + var_export($title, true), + gettype($title) + ), __LINE__ + ); + } + + $this->title = $title; + return $this; + } + + /** + * Get orderNo value + * + * @return string|null + */ + public function getOrderNo(): string + { + return $this->orderNo; + } + + /** + * Set title value + * + * @param string $orderNo + * + * @return CreateOrder + */ + public function setOrderNo(string $orderNo = ''): CreateOrder + { + // validation for constraint: string + if (!is_string($orderNo)) { + throw new InvalidArgumentException( + sprintf( + 'Invalid value %s, please provide a string, %s given', + var_export($orderNo, true), + gettype($orderNo) + ), __LINE__ + ); + } + + $this->orderNo = $orderNo; + return $this; + } + + /** + * Get mount value + * + * @return string|null + */ + public function getAmount(): string + { + return $this->amount; + } + + /** + * Set mount value + * + * @param float $mount + * + * @return CreateOrder + */ + public function setAmount(string $mount = '0'): CreateOrder + { + // validation for constraint: falot + if (!is_string($mount)) { + throw new InvalidArgumentException( + sprintf( + 'Invalid value %s, please provide a string, %s given', + var_export($mount, true), + gettype($mount) + ), __LINE__ + ); + } + + $this->amount = $mount; + return $this; + } + + /** + * Get tag value + * + * @return string|null + */ + public function getTag(): string + { + return $this->tag; + } + + /** + * Set tag value + * + * @param string $tag + * + * @return CreateOrder + */ + public function setTag(string $tag = ''): CreateOrder + { + // validation for constraint: string + if (!is_string($tag)) { + throw new InvalidArgumentException( + sprintf('Invalid value %s, please provide a string, %s given', var_export($tag, true), gettype($tag)), + __LINE__ + ); + } + + $this->tag = $tag; + return $this; + } + + /** + * Get attach value + * + * @return string|null + */ + public function getAttach(): string + { + return $this->attach; + } + + /** + * Set attach value + * + * @param string $attach + * + * @return CreateOrder + */ + public function setAttach(string $attach = ''): CreateOrder + { + // validation for constraint: string + if (!is_string($attach)) { + throw new InvalidArgumentException( + sprintf( + 'Invalid value %s, please provide a string, %s given', + var_export($attach, true), + gettype($attach) + ), __LINE__ + ); + } + + $this->attach = $attach; + return $this; + } + + /** + * Get orderType value + * + * @return string|null + */ + public function getOrderType(): string + { + return $this->orderType; + } + + /** + * Set orderType value + * + * @param string $orderType + * + * @return CreateOrder + */ + public function setOrderType(string $orderType = ''): CreateOrder + { + // validation for constraint: string + if (!is_string($orderType)) { + throw new InvalidArgumentException( + sprintf( + 'Invalid value %s, please provide a string, %s given', + var_export($orderType, true), + gettype($orderType) + ), __LINE__ + ); + } + + $this->orderType = $orderType; + return $this; + } + + /** + * Get openId value + * + * @return string|null + */ + public function getOpenId(): string + { + return $this->openId; + } + + /** + * Set openId value + * + * @param string $openId + * + * @return CreateOrder + */ + public function setOpenId(string $openId = ''): CreateOrder + { + // validation for constraint: string + if (!is_string($openId)) { + throw new InvalidArgumentException( + sprintf( + 'Invalid value %s, please provide a string, %s given', + var_export($openId, true), + gettype($openId) + ), __LINE__ + ); + } + + $this->openId = $openId; + return $this; + } + + /** + * Get notifyUrl value + * + * @return string|null + */ + public function getNotifyUrl(): string + { + return $this->notifyUrl; + } + + /** + * Set notifyUrl value + * + * @param string $notifyUrl + * + * @return CreateOrder + */ + public function setNotifyUrl(string $notifyUrl = ''): CreateOrder + { + // validation for constraint: string + if (!is_string($notifyUrl)) { + throw new InvalidArgumentException( + sprintf( + 'Invalid value %s, please provide a string, %s given', + var_export($notifyUrl, true), + gettype($notifyUrl) + ), __LINE__ + ); + } + + $this->notifyUrl = $notifyUrl; + return $this; + } +} \ No newline at end of file diff --git a/packagist/unify_payment/src/Cores/Struct/QueryOrder.php b/packagist/unify_payment/src/Cores/Struct/QueryOrder.php new file mode 100644 index 0000000..fefb594 --- /dev/null +++ b/packagist/unify_payment/src/Cores/Struct/QueryOrder.php @@ -0,0 +1,65 @@ +setOrderNo($orderNo); + } + + /** + * Get orderNo value + * + * @return string|null + */ + public function getOrderNo(): string + { + return $this->orderNo; + } + + /** + * Set orderNo value + * + * @param string $orderNo + * + * @return QueryOrder + */ + public function setOrderNo(string $orderNo = ''): QueryOrder + { + // validation for constraint: string + if (!is_string($orderNo)) { + throw new InvalidArgumentException( + sprintf( + 'Invalid value %s, please provide a string, %s given', + var_export($orderNo, true), + gettype($orderNo) + ), __LINE__ + ); + } + + $this->orderNo = $orderNo; + return $this; + } +} \ No newline at end of file diff --git a/packagist/unify_payment/src/Cores/Struct/RefundOrder.php b/packagist/unify_payment/src/Cores/Struct/RefundOrder.php new file mode 100644 index 0000000..617f023 --- /dev/null +++ b/packagist/unify_payment/src/Cores/Struct/RefundOrder.php @@ -0,0 +1,192 @@ +setOrderNo($orderNo) + ->setRefundNo($refundNo) + ->setRefundAmount($refundAmount) + ->setRefundReason($refundReason); + } + + /** + * Get orderNo value + * + * @return string|null + */ + public function getOrderNo(): string + { + return $this->orderNo; + } + + /** + * Set orderNo value + * + * @param string $orderNo + * + * @return RefundOrder + */ + public function setOrderNo(string $orderNo = ''): RefundOrder + { + // validation for constraint: string + if (!is_string($orderNo)) { + throw new InvalidArgumentException( + sprintf( + 'Invalid value %s, please provide a string, %s given', + var_export($orderNo, true), + gettype($orderNo) + ), __LINE__ + ); + } + + $this->orderNo = $orderNo; + return $this; + } + + /** + * Get refundNo value + * + * @return string|null + */ + public function getRefundNo(): string + { + return $this->refundNo; + } + + /** + * Set refundNo value + * + * @param string $refundNo + * + * @return RefundOrder + */ + public function setRefundNo(string $refundNo = ''): RefundOrder + { + // validation for constraint: string + if (!is_string($refundNo)) { + throw new InvalidArgumentException( + sprintf( + 'Invalid value %s, please provide a string, %s given', + var_export($refundNo, true), + gettype($refundNo) + ), __LINE__ + ); + } + + $this->refundNo = $refundNo; + return $this; + } + + /** + * Get refundAmount value + * + * @return string|null + */ + public function getRefundAmount(): string + { + return $this->refundAmount; + } + + /** + * Set refundAmount value + * + * @param string $refundAmount + * + * @return RefundOrder + */ + public function setRefundAmount(string $refundAmount = '0'): RefundOrder + { + // validation for constraint: float + if (!is_string($refundAmount)) { + throw new InvalidArgumentException( + sprintf( + 'Invalid value %s, please provide a string, %s given', + var_export($refundAmount, true), + gettype($refundAmount) + ), __LINE__ + ); + } + + $this->refundAmount = $refundAmount; + return $this; + } + + /** + * Get refundReason value + * + * @return string|null + */ + public function getRefundReason(): string + { + return $this->refundReason; + } + + /** + * Set refundReason value + * + * @param string $refundReason + * + * @return RefundOrder + */ + public function setRefundReason(string $refundReason = ''): RefundOrder + { + // validation for constraint: string + if (!is_string($refundReason)) { + throw new InvalidArgumentException( + sprintf( + 'Invalid value %s, please provide a string, %s given', + var_export($refundReason, true), + gettype($refundReason) + ), __LINE__ + ); + } + + $this->refundReason = $refundReason; + return $this; + } +} \ No newline at end of file diff --git a/packagist/unify_payment/src/Cores/Support/Arr.php b/packagist/unify_payment/src/Cores/Support/Arr.php new file mode 100644 index 0000000..c6bae7d --- /dev/null +++ b/packagist/unify_payment/src/Cores/Support/Arr.php @@ -0,0 +1,609 @@ + $value) { + [$innerKey, $innerValue] = $callback($key, $value); + $results[$innerKey] = $innerValue; + } + + return $results; + } + + /** + * Divide an array into two arrays. One with keys and the other with values. + */ + public static function divide(array $array): array + { + return [ + array_keys($array), + array_values($array), + ]; + } + + /** + * Flatten a multi-dimensional associative array with dots. + */ + public static function dot(array $array, string $prepend = ''): array + { + $results = []; + + foreach ($array as $key => $value) { + if (is_array($value)) { + $results = array_merge($results, static::dot($value, $prepend . $key . '.')); + } else { + $results[$prepend . $key] = $value; + } + } + + return $results; + } + + /** + * Get all of the given array except for a specified array of items. + * + * @param array|string $keys + */ + public static function except(array $array, $keys): array + { + return array_diff_key($array, array_flip((array)$keys)); + } + + /** + * access array. + * + * if not array access, return original. + * + * @param mixed $data + * + * @return mixed + * + */ + public static function access($data) + { + if (!self::accessible($data) && + !(is_object($data) && method_exists($data, 'toArray'))) { + return $data; + } + + return is_object($data) ? $data->toArray() : $data; + } + + /** + * Determine if the given key exists in the provided array. + * + * @param \ArrayAccess|array $array + * @param string|int $key + * + * @return bool + */ + public static function exists($array, $key) + { + $array = self::access($array); + + if ($array instanceof ArrayAccess) { + return $array->offsetExists($key); + } + + return array_key_exists($key, $array); + } + + /** + * Check if an item or items exist in an array using "dot" notation. + * + * @param \ArrayAccess|array $array + * @param string|array $keys + * + * @return bool + */ + public static function has($array, $keys) + { + $array = self::access($array); + + $keys = (array)$keys; + + if (!$array || $keys === []) { + return false; + } + + foreach ($keys as $key) { + $subKeyArray = $array; + + if (static::exists($array, $key)) { + continue; + } + + foreach (explode('.', $key) as $segment) { + if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) { + $subKeyArray = $subKeyArray[$segment]; + } else { + return false; + } + } + } + + return true; + } + + /** + * Determine if any of the keys exist in an array using "dot" notation. + * + * @param \ArrayAccess|array $array + * @param string|array $keys + * + * @return bool + */ + public static function hasAny($array, $keys) + { + $array = self::access($array); + + if (is_null($keys)) { + return false; + } + + $keys = (array)$keys; + + if (!$array) { + return false; + } + + if ($keys === []) { + return false; + } + + foreach ($keys as $key) { + if (static::has($array, $key)) { + return true; + } + } + + return false; + } + + /** + * Fetch a flattened array of a nested array element. + */ + public static function fetch(array $array, string $key): array + { + $results = []; + + foreach (explode('.', $key) as $segment) { + $results = []; + foreach ($array as $value) { + $value = (array)$value; + $results[] = $value[$segment]; + } + $array = array_values($results); + } + + return array_values($results); + } + + /** + * Return the first element in an array passing a given truth test. + * + * @param mixed $default + * + * @return mixed + */ + public static function first(array $array, callable $callback, $default = null) + { + foreach ($array as $key => $value) { + if (call_user_func($callback, $key, $value)) { + return $value; + } + } + + return $default; + } + + /** + * Return the last element in an array passing a given truth test. + * + * @param mixed $default + * + * @return mixed + */ + public static function last(array $array, callable $callback, $default = null) + { + return static::first(array_reverse($array), $callback, $default); + } + + /** + * Flatten a multi-dimensional array into a single level. + */ + public static function flatten(array $array): array + { + $return = []; + array_walk_recursive( + $array, + function ($x) use (&$return) { + $return[] = $x; + } + ); + + return $return; + } + + /** + * Remove one or many array items from a given array using "dot" notation. + * + * @param array $array + * @param array|string $keys + */ + public static function forget(&$array, $keys) + { + $original = &$array; + + $keys = (array)$keys; + + if (0 === count($keys)) { + return; + } + + foreach ($keys as $key) { + // if the exact key exists in the top-level, remove it + if (static::exists($array, $key)) { + unset($array[$key]); + + continue; + } + + $parts = explode('.', $key); + + // clean up before each pass + $array = &$original; + + while (count($parts) > 1) { + $part = array_shift($parts); + + if (isset($array[$part]) && is_array($array[$part])) { + $array = &$array[$part]; + } else { + continue 2; + } + } + + unset($array[array_shift($parts)]); + } + } + + /** + * Get an item from an array using "dot" notation. + * + * @param mixed $default + * + * @return mixed + */ + public static function get(array $array, string $key, $default = null) + { + if (is_null($key)) { + return $array; + } + + if (isset($array[$key])) { + return $array[$key]; + } + + foreach (explode('.', $key) as $segment) { + if (!is_array($array) || !array_key_exists($segment, $array)) { + return $default; + } + $array = $array[$segment]; + } + + return $array; + } + + /** + * Get a subset of the items from the given array. + * + * @param array|string $keys + */ + public static function only(array $array, $keys): array + { + return array_intersect_key($array, array_flip((array)$keys)); + } + + /** + * Pluck an array of values from an array. + * + * @param string $key + */ + public static function pluck(array $array, string $value, string $key = null): array + { + $results = []; + + foreach ($array as $item) { + $itemValue = is_object($item) ? $item->{$value} : $item[$value]; + // If the key is "null", we will just append the value to the array and keep + // looping. Otherwise we will key the array using the value of the key we + // received from the developer. Then we'll return the final array form. + if (is_null($key)) { + $results[] = $itemValue; + } else { + $itemKey = is_object($item) ? $item->{$key} : $item[$key]; + $results[$itemKey] = $itemValue; + } + } + + return $results; + } + + /** + * Push an item onto the beginning of an array. + * + * @param mixed $value + * @param mixed $key + * + * @return array + */ + public static function prepend(array $array, $value, $key = null) + { + if (is_null($key)) { + array_unshift($array, $value); + } else { + $array = [$key => $value] + $array; + } + + return $array; + } + + /** + * Get a value from the array, and remove it. + * + * @param mixed $default + * + * @return mixed + */ + public static function pull(array &$array, string $key, $default = null) + { + $value = static::get($array, $key, $default); + + static::forget($array, $key); + + return $value; + } + + /** + * Get one or a specified number of random values from an array. + * + * @param array $array + * @param int|null $number + * + * @return mixed + * + * @throws \InvalidArgumentException + */ + public static function random(array $array, $number = null) + { + $requested = is_null($number) ? 1 : $number; + + $count = count($array); + + $number = $requested > $count ? $count : $requested; + + if (is_null($number)) { + return $array[array_rand($array)]; + } + + if (0 === (int)$number) { + return []; + } + + $keys = array_rand($array, $number); + + $results = []; + + foreach ((array)$keys as $key) { + $results[] = $array[$key]; + } + + return $results; + } + + /** + * Set an array item to a given value using "dot" notation. + * + * If no key is given to the method, the entire array will be replaced. + * + * @param mixed $value + */ + public static function set(array &$array, string $key, $value): array + { + if (is_null($key)) { + return $array = $value; + } + + $keys = explode('.', $key); + + while (count($keys) > 1) { + $key = array_shift($keys); + // If the key doesn't exist at this depth, we will just create an empty array + // to hold the next value, allowing us to create the arrays to hold final + // values at the correct depth. Then we'll keep digging into the array. + if (!isset($array[$key]) || !is_array($array[$key])) { + $array[$key] = []; + } + $array = &$array[$key]; + } + $array[array_shift($keys)] = $value; + + return $array; + } + + /** + * Sort the array using the given Closure. + */ + public static function sort(array $array, callable $callback): array + { + $results = []; + + foreach ($array as $key => $value) { + $results[$key] = $callback($value); + } + + return $results; + } + + /** + * Shuffle the given array and return the result. + * + * @param array $array + * @param int|null $seed + * + * @return array + */ + public static function shuffle(array $array, $seed = null): array + { + if (is_null($seed)) { + shuffle($array); + } else { + mt_srand($seed); + shuffle($array); + mt_srand(); + } + + return $array; + } + + /** + * Convert the array into a query string. + */ + public static function query(array $array): string + { + return http_build_query($array, null, '&', PHP_QUERY_RFC3986); + } + + /** + * Filter the array using the given callback. + */ + public static function where(array $array, ?callable $callback = null): array + { + return array_filter( + $array, + $callback ?? function ($value) use ($callback) { + if (static::accessible($value)) { + $value = static::where($value, $callback); + } + + if (is_array($value) && 0 === count($value)) { + $value = null; + } + + return '' !== $value && !is_null($value); + }, + ARRAY_FILTER_USE_BOTH + ); + } + + /** + * Convert encoding. + * + * @param string $from_encoding + * + * @author yansongda + * + */ + public static function encoding(array $array, string $to_encoding, $from_encoding = 'gb2312'): array + { + $encoded = []; + + foreach ($array as $key => $value) { + $encoded[$key] = is_array($value) ? self::encoding($value, $to_encoding, $from_encoding) : + mb_convert_encoding($value, $to_encoding, $from_encoding); + } + + return $encoded; + } + + /** + * camelCaseKey. + * + * @param mixed $data + * + * @return mixed + * @author yansongda + * + */ + public static function camelCaseKey($data) + { + if (!self::accessible($data) && + !(is_object($data) && method_exists($data, 'toArray'))) { + return $data; + } + + $result = []; + $data = self::access($data); + + foreach ($data as $key => $value) { + $result[is_string($key) ? Str::camel($key) : $key] = self::camelCaseKey($value); + } + + return $result; + } + + /** + * snakeCaseKey. + * + * @param mixed $data + * + * @return mixed + * @author yansongda + * + */ + public static function snakeCaseKey($data) + { + if (!self::accessible($data) && + !(is_object($data) && method_exists($data, 'toArray'))) { + return $data; + } + + $data = self::access($data); + $result = []; + + foreach ($data as $key => $value) { + $result[is_string($key) ? Str::snake($key) : $key] = self::snakeCaseKey($value); + } + + return $result; + } +} diff --git a/packagist/unify_payment/src/Cores/Support/Logger.php b/packagist/unify_payment/src/Cores/Support/Logger.php new file mode 100644 index 0000000..3a82edb --- /dev/null +++ b/packagist/unify_payment/src/Cores/Support/Logger.php @@ -0,0 +1,169 @@ + 'unify', + 'timezone' => 'Asia/Shanghai', + 'date_format' => 'Y-m-d H:i:s', + 'formatter' => LineFormatter::class, + 'formatter_with' => [], + 'handler' => RotatingFileHandler::class, + 'handler_with' => [] + ]; + + /** + * Set Logger + * + * @param LoggerInterface $logger + * + * @return $this + */ + public function setLogger(LoggerInterface $logger): Logger + { + $this->logger = $logger; + + return $this; + } + + /** + * Get Logger + * + * @return LoggerInterface + * + * @throws InvalidConfigException + */ + public function getLogger(): LoggerInterface + { + if (is_null($this->logger)) { + $this->logger = $this->createLogger(); + } + + return $this->logger; + } + + /** + * Create logger + * + * @return MonoLogger + * + * @throws InvalidConfigException + */ + protected function createLogger(): MonoLogger + { + return new MonoLogger( + $this->config['channel'], + [$this->createHandler()], + [], + new DateTimeZone($this->config['timezone']) + ); + } + + /** + * Create handler + * + * @return HandlerInterface + * + * @throws InvalidConfigException + */ + protected function createHandler(): HandlerInterface + { + if (!is_a($this->config['handler'], HandlerInterface::class, true)) { + throw new InvalidConfigException( + $this->config['handler'] . ' must be an instance of ' . HandlerInterface::class + ); + } + + $handler = new $this->config['handler'](... array_values($this->config['handler_with'])); + $handler->setFormatter($this->createFormatter()); + + return $handler; + } + + /** + * Create formatter + * + * @return FormatterInterface + * + * @throws InvalidConfigException + */ + protected function createFormatter(): FormatterInterface + { + if (!is_a($this->config['formatter'], FormatterInterface::class, true)) { + throw new InvalidConfigException( + $this->config['formatter'] . ' must be an instance of ' . FormatterInterface::class + ); + } + + return new $this->config['formatter'](... array_values($this->config['formatter_with'])); + } + + /** + * Call user function + * + * @param string $method + * @param array $parameters + * + * @return mixed + * @throws InvalidConfigException + */ + public function __call(string $method, array $parameters): void + { + call_user_func_array([$this->getLogger(), $method], $parameters); + } + + /** + * Set config + * + * @param array $config + * + * @return self + */ + public function setConfig(array $config): self + { + $this->config = array_merge($this->config, $config); + + return $this; + } + + /** + * Get config + * + */ + public function getConfig(): array + { + return $this->config; + } + +} \ No newline at end of file diff --git a/packagist/unify_payment/src/Cores/Support/Str.php b/packagist/unify_payment/src/Cores/Support/Str.php new file mode 100644 index 0000000..a532ed7 --- /dev/null +++ b/packagist/unify_payment/src/Cores/Support/Str.php @@ -0,0 +1,184 @@ +getProperties() as $item) { + $k = $item->getName(); + $method = 'get' . Str::studly($k); + + $result[$k] = method_exists($this, $method) ? $this->{$method}() : $this->{$k}; + } + + return $result; + } +} diff --git a/packagist/unify_payment/src/Cores/Traits/HttpRequest.php b/packagist/unify_payment/src/Cores/Traits/HttpRequest.php new file mode 100644 index 0000000..fefde66 --- /dev/null +++ b/packagist/unify_payment/src/Cores/Traits/HttpRequest.php @@ -0,0 +1,240 @@ + '', + 'timeout' => 0, + 'connect_timeout' => 0 + ]; + + /** + * Send a GET request. + * + * @return array|string + */ + public function get(string $endpoint, array $query = [], array $headers = []) + { + return $this->request('GET', $endpoint, [ + 'headers' => $headers, + 'query' => $query, + ]); + } + + /** + * Send a POST request. + * + * @param string|array $data + * + * @return array|string + */ + public function post(string $endpoint, $data, array $options = []) + { + if (!is_array($data)) { + $options['body'] = $data; + } else { + $options['form_params'] = $data; + } + + return $this->request('POST', $endpoint, $options); + } + + /** + * Send request. + * + * @return array|string + */ + public function request(string $method, string $endpoint, array $options = []) + { + return $this->unwrapResponse($this->getHttpClient()->{$method}($endpoint, $options)); + } + + /** + * Convert response. + * + * @return array|string + */ + public function unwrapResponse(ResponseInterface $response) + { + $contentType = $response->getHeaderLine('Content-Type'); + $contents = $response->getBody()->getContents(); + + if (false !== stripos($contentType, 'json') || stripos($contentType, 'javascript')) { + return json_decode($contents, true); + } + + if (false !== stripos($contentType, 'xml')) { + return json_decode( + json_encode( + simplexml_load_string($contents, 'SimpleXMLElement', LIBXML_NOCDATA), + JSON_UNESCAPED_UNICODE + ), + true + ); + } + + return $contents; + } + + /** + * Set http client. + * + * @return $this + */ + public function setHttpClient(Client $client): self + { + $this->httpClient = $client; + + return $this; + } + + /** + * Get http client. + * + * @return Client + */ + public function getHttpClient(): Client + { + if (is_null($this->httpClient)) { + $this->httpClient = $this->getDefaultHttpClient(); + } + + return $this->httpClient; + } + + /** + * Get default http client. + * + * @return Client + */ + public function getDefaultHttpClient(): Client + { + return new Client($this->getOptions()); + } + + /** + * set mock Handler + * + * @param Response[] $mock + * + * @return HttpRequest|BasicClient + */ + public function setMockHandler(array $mock): self + { + array_walk($mock, static function ($value) { + if (!is_subclass_of($value, ResponseInterface::class)) { + throw new InvalidArgumentException( + $value . ' must be an instance of ' . ResponseInterface::class + ); + } + }); + + $this->mockHandler = new MockHandler($mock); + $handlerStack = HandlerStack::create($this->mockHandler); + $this->setHttpOptions(['handler' => $handlerStack]); + + return $this; + } + + /** + * Get mock handler + * + * @return MockHandler|null + */ + public function getMockHandler(): ?MockHandler + { + return $this->mockHandler; + } + + /** + * Get default options. + * + * @return array + */ + public function getOptions(): array + { + return $this->getHttpOptions(); + } + + /** + * Set http header. + * + * @return $this + */ + public function setHttpHeader(array $httpHeader): self + { + $this->httpHeader = array_merge($httpHeader, $this->httpHeader); + + return $this; + } + + /** + * Get http header. + * + * @return array + */ + public function getHttpHeader(): array + { + return $this->httpHeader; + } + + /** + * Set http options. + * + * @param array $httpOptions + * + * @return void + */ + public function setHttpOptions(array $httpOptions): void + { + $this->httpOptions = array_merge($this->httpOptions, $httpOptions); + } + + /** + * Get http options. + * + * @return array + */ + public function getHttpOptions(): array + { + return $this->httpOptions; + } +} \ No newline at end of file diff --git a/packagist/unify_payment/src/Cores/Traits/InteractWithConfig.php b/packagist/unify_payment/src/Cores/Traits/InteractWithConfig.php new file mode 100644 index 0000000..e3bc10d --- /dev/null +++ b/packagist/unify_payment/src/Cores/Traits/InteractWithConfig.php @@ -0,0 +1,47 @@ +config = is_array($config) ? new Config($config) : $config; + } + + /** + * Get Config + * + * @return ConfigInterface + */ + public function getConfig(): ConfigInterface + { + return $this->config; + } + + /** + * Set Config. + * + * @param ConfigInterface $config + * + * @return $this + */ + public function setConfig(ConfigInterface $config): self + { + $this->config = $config; + + return $this; + } +} diff --git a/packagist/unify_payment/src/Factory.php b/packagist/unify_payment/src/Factory.php new file mode 100644 index 0000000..c71a120 --- /dev/null +++ b/packagist/unify_payment/src/Factory.php @@ -0,0 +1,77 @@ +config = new Config($config); + + $this->registerLogService(); + } + + /** + * Dynamically pass methods to the application. + * + * @param string $name + * @param array $arguments + * + * @return mixed + * + * @throws ClassNotExistsException + */ + public static function __callStatic(string $name, array $arguments) + { + return (new self(... $arguments))->make($name); + } + + /** + * Make Application. + * + * @param string $name + * + * @return mixed + * + * @throws ClassNotExistsException + */ + public function make(string $name) + { + $namespace = Str::studly($name); + $application = "\\UnifyPayment\\Modules\\{$namespace}\\Application"; + + if (!class_exists($application)) { + throw new ClassNotExistsException("Class [{$name}] Not Exists"); + } + + return new $application($this->config); + } + + /** + * Register log Service + * + * @return void + */ + public function registerLogService(): void + { + $logger = new Logger(); + $logger->setConfig($this->config->get('log')); + + Log::setInstance($logger); + } +} \ No newline at end of file diff --git a/packagist/unify_payment/src/Mock/CloseOrderHandler.php b/packagist/unify_payment/src/Mock/CloseOrderHandler.php new file mode 100644 index 0000000..e7e0d93 --- /dev/null +++ b/packagist/unify_payment/src/Mock/CloseOrderHandler.php @@ -0,0 +1,47 @@ +faker->randomDigit(); + + $data = array_merge($this->getSuccessHeaderData('Query Success.'), [ + 'response' => [ + 'orderNo' => 'FD' . $channelId . $this->faker->time('YmdHis') . $this->faker->regexify('[0-9]{5}'), + 'closeTime' => $this->faker->time('Y-m-d H:i:s'), + 'merchantId' => 'STZL' . $this->faker->time('Ymd'), + 'merchantName' => $this->faker->company(), + 'channelId' => $channelId, + 'channelName' => $this->faker->randomElement(['公众号支付', '小程序支付']) + ] + ]); + + return [ + self::SUCCESS_CODE, + self::JSON_HEADER, + json_encode($data, JSON_UNESCAPED_UNICODE) + ]; + } + + /** + * @return array + */ + public function failure(): array + { + $data = $this->getFailureHeaderData($this->faker->sentence()); + + return [ + self::FAILURE_CODE, + self::JSON_HEADER, + json_encode($data, JSON_UNESCAPED_UNICODE) + ]; + } +} \ No newline at end of file diff --git a/packagist/unify_payment/src/Mock/ConfirmOrderForExHandler.php b/packagist/unify_payment/src/Mock/ConfirmOrderForExHandler.php new file mode 100644 index 0000000..ca4c008 --- /dev/null +++ b/packagist/unify_payment/src/Mock/ConfirmOrderForExHandler.php @@ -0,0 +1,48 @@ +faker->randomDigit(); + + $data = array_merge($this->getSuccessHeaderData('Query Success.'), [ + 'response' => [ + 'orderNo' => 'FD' . $channelId . $this->faker->time('YmdHis') . $this->faker->regexify('[0-9]{5}'), + 'hisSerialNo' => $this->faker->regexify('[0-9]{7}'), + 'confirmTime' => $this->faker->time('Y-m-d H:i:s'), + 'merchantId' => 'STZL' . $this->faker->time('Ymd'), + 'merchantName' => $this->faker->company(), + 'channelId' => $channelId, + 'channelName' => $this->faker->randomElement(['公众号支付', '小程序支付']) + ] + ]); + + return [ + self::SUCCESS_CODE, + self::JSON_HEADER, + json_encode($data, JSON_UNESCAPED_UNICODE) + ]; + } + + /** + * @return array + */ + public function failure(): array + { + $data = $this->getFailureHeaderData($this->faker->sentence()); + + return [ + self::FAILURE_CODE, + self::JSON_HEADER, + json_encode($data, JSON_UNESCAPED_UNICODE) + ]; + } +} \ No newline at end of file diff --git a/packagist/unify_payment/src/Mock/CreateOrderHandler.php b/packagist/unify_payment/src/Mock/CreateOrderHandler.php new file mode 100644 index 0000000..12971c8 --- /dev/null +++ b/packagist/unify_payment/src/Mock/CreateOrderHandler.php @@ -0,0 +1,49 @@ +getSuccessHeaderData('Create Success.'), [ + 'response' => [ + 'appId' => 'wx' . $this->faker->regexify('[0-9a-z]{15}'), + 'timeStamp' => $this->faker->unixTime(), + 'nonceStr' => $this->faker->regexify('[0-9a-zA-Z]{16}'), + 'package' => 'prepay_id=wx' . $this->faker->regexify('[0-9a-zA-Z]{34}'), + 'signType' => 'MD5', + 'paySign' => $this->faker->md5(), + 'merchantId' => 'STZL' . $this->faker->time('Ymd'), + 'merchantName' => $this->faker->company(), + 'channelId' => 'FD00' . $this->faker->randomDigit(), + 'channelName' => $this->faker->randomElement(['公众号支付', '小程序支付']) + ] + ]); + + return [ + self::SUCCESS_CODE, + self::JSON_HEADER, + json_encode($data, JSON_UNESCAPED_UNICODE) + ]; + } + + /** + * @return array + */ + public function failure(): array + { + $data = $this->getFailureHeaderData($this->faker->sentence()); + + return [ + self::FAILURE_CODE, + self::JSON_HEADER, + json_encode($data, JSON_UNESCAPED_UNICODE) + ]; + } +} \ No newline at end of file diff --git a/packagist/unify_payment/src/Mock/QueryOrderHandler.php b/packagist/unify_payment/src/Mock/QueryOrderHandler.php new file mode 100644 index 0000000..15bea99 --- /dev/null +++ b/packagist/unify_payment/src/Mock/QueryOrderHandler.php @@ -0,0 +1,53 @@ +faker->randomDigit(); + $status = $this->faker->randomElement(self::TRADE_STATE); + + $data = array_merge($this->getSuccessHeaderData('Create Success.'), [ + 'response' => [ + 'orderNo' => 'FD' . $channelId . $this->faker->time('YmdHis') . $this->faker->regexify('[0-9]{5}'), + 'payAccount' => $this->faker->email(), + 'transNo' => $this->faker->time('YmdHis') . $this->faker->regexify('[0-9]{10}'), + 'status' => $status, + 'statusDesc' => self::TRADE_STATE_DESC[$status], + 'amount' => $this->faker->randomFloat(2), + 'tag' => $this->faker->numerify('tag###'), + 'merchantId' => 'STZL' . $this->faker->time('Ymd'), + 'merchantName' => $this->faker->company(), + 'channelId' => $channelId, + 'channelName' => $this->faker->randomElement(['公众号支付', '小程序支付']) + ] + ]); + + return [ + self::SUCCESS_CODE, + self::JSON_HEADER, + json_encode($data, JSON_UNESCAPED_UNICODE) + ]; + } + + /** + * @return array + */ + public function failure(): array + { + $data = $this->getFailureHeaderData($this->faker->sentence()); + + return [ + self::FAILURE_CODE, + self::JSON_HEADER, + json_encode($data, JSON_UNESCAPED_UNICODE) + ]; + } +} \ No newline at end of file diff --git a/packagist/unify_payment/src/Mock/RefundOrderHandler.php b/packagist/unify_payment/src/Mock/RefundOrderHandler.php new file mode 100644 index 0000000..6fb6c12 --- /dev/null +++ b/packagist/unify_payment/src/Mock/RefundOrderHandler.php @@ -0,0 +1,50 @@ +faker->randomDigit(); + $orderNo = 'FD' . $channelId . $this->faker->time('YmdHis') . $this->faker->regexify('[0-9]{5}'); + + $data = array_merge($this->getSuccessHeaderData('Query Success.'), [ + 'response' => [ + 'orderNo' => $orderNo, + 'refundNo' => $orderNo . '_' . $this->faker->regexify('[0-9]{5}'), + 'refundAmount' => $this->faker->randomFloat(2), + 'refundTime' => $this->faker->time('Y-m-d H:i:s'), + 'merchantId' => 'STZL' . $this->faker->time('Ymd'), + 'merchantName' => $this->faker->company(), + 'channelId' => $channelId, + 'channelName' => $this->faker->randomElement(['公众号支付', '小程序支付']) + ] + ]); + + return [ + self::SUCCESS_CODE, + self::JSON_HEADER, + json_encode($data, JSON_UNESCAPED_UNICODE) + ]; + } + + /** + * @return array + */ + public function failure(): array + { + $data = $this->getFailureHeaderData($this->faker->sentence()); + + return [ + self::FAILURE_CODE, + self::JSON_HEADER, + json_encode($data, JSON_UNESCAPED_UNICODE) + ]; + } +} \ No newline at end of file diff --git a/packagist/unify_payment/src/Mock/ResponseHandler.php b/packagist/unify_payment/src/Mock/ResponseHandler.php new file mode 100644 index 0000000..ef9e1b9 --- /dev/null +++ b/packagist/unify_payment/src/Mock/ResponseHandler.php @@ -0,0 +1,118 @@ + 'application/json']; + + /** + * 交易状态 + */ + const TRADE_STATE = [ + 'SUCCESS', + 'REFUND', + 'NOTPAY', + 'CLOSED', + 'REVOKED', + 'USERPAYING', + 'PAYERROR' + ]; + + /** + * 交易状态描述 + */ + const TRADE_STATE_DESC = [ + 'SUCCESS' => '支付成功', + 'REFUND' => '转入退款', + 'NOTPAY' => '未支付', + 'CLOSED' => '已关闭', + 'REVOKED' => '已撤销', + 'USERPAYING' => '用户支付中', + 'PAYERROR' => '支付失败' + ]; + + /** + * Faker. + * + * @var Generator + */ + protected Generator $faker; + + /** + * Response Construct. + * + * @param bool $successful + */ + public function __construct(bool $successful) + { + $this->faker = Factory::create(); + + if ($successful) { + $resData = $this->success(); + } else { + $resData = $this->failure(); + } + + parent::__construct(... $resData); + } + + /** + * + * @param string $msg + * + * @return array + */ + protected function getSuccessHeaderData(string $msg): array + { + return [ + 'status' => self::SUCCESS_CODE, + 'tag' => 'SUCCESS', + 'success' => true, + 'msg' => 'msg', + 'response' => null + ]; + } + + /** + * + * @param string $msg + * + * @return array + */ + protected function getFailureHeaderData(string $msg): array + { + return [ + 'status' => self::FAILURE_CODE, + 'tag' => 'FAILURE', + 'success' => false, + 'msg' => 'msg', + 'response' => null + ]; + } + + /** + * Success Response. + * + * @return mixed + */ + abstract public function success(); + + /** + * Fail Response. + * + * @return mixed + */ + abstract public function failure(); +} \ No newline at end of file diff --git a/packagist/unify_payment/src/Modules/Common/Application.php b/packagist/unify_payment/src/Modules/Common/Application.php new file mode 100644 index 0000000..9a68228 --- /dev/null +++ b/packagist/unify_payment/src/Modules/Common/Application.php @@ -0,0 +1,20 @@ + OrderClient::class, + ]; +} \ No newline at end of file diff --git a/packagist/unify_payment/src/Modules/Common/OrderClient.php b/packagist/unify_payment/src/Modules/Common/OrderClient.php new file mode 100644 index 0000000..d8cdad8 --- /dev/null +++ b/packagist/unify_payment/src/Modules/Common/OrderClient.php @@ -0,0 +1,85 @@ +payload = array_merge($this->payload, $order->toArray()); + $this->payload['apiSecret'] = $this->config->get('apiSecret'); + + return $this->request('post', '/index.php/api/pay/query'); + } + + /** + * Close Order. + * + * @param CloseOrder $order + * + * @return array|string + * + * @throws ReflectionException + */ + public function close(CloseOrder $order) + { + $this->payload = array_merge($this->payload, $order->toArray()); + $this->payload['apiSecret'] = $this->config->get('apiSecret'); + + return $this->request('post', '/index.php/api/pay/close'); + } + + + /** + * Refund Order. + * + * @param RefundOrder $order + * + * @return array|string + * + * @throws ReflectionException + */ + public function refund(RefundOrder $order) + { + $this->payload = array_merge($this->payload, $order->toArray()); + $this->payload['apiSecret'] = $this->config->get('apiSecret'); + + return $this->request('post', '/index.php/api/pay/refund'); + } + + /** + * Confirm Order For Extends. + * + * @param ConfirmOrderForEx $order + * + * @return array|string + * + * @throws ReflectionException + */ + public function confirmForEx(ConfirmOrderForEx $order) + { + $this->payload = array_merge($this->payload, $order->toArray()); + $this->payload['apiSecret'] = $this->config->get('apiSecret'); + + return $this->request('post', '/index.php/api/pay/confirmForEx'); + } +} \ No newline at end of file diff --git a/packagist/unify_payment/src/Modules/Pay/Application.php b/packagist/unify_payment/src/Modules/Pay/Application.php new file mode 100644 index 0000000..d21c759 --- /dev/null +++ b/packagist/unify_payment/src/Modules/Pay/Application.php @@ -0,0 +1,22 @@ + OfficialAccountClient::class, + 'mini' => MiniProgramClient::class + ]; +} \ No newline at end of file diff --git a/packagist/unify_payment/src/Modules/Pay/MiniProgramClient.php b/packagist/unify_payment/src/Modules/Pay/MiniProgramClient.php new file mode 100644 index 0000000..9556866 --- /dev/null +++ b/packagist/unify_payment/src/Modules/Pay/MiniProgramClient.php @@ -0,0 +1,35 @@ +payload = array_merge($this->payload, $order->toArray()); + $this->payload['sign'] = $this->generateSign($this->payload); + + return $this->request('post', '/index.php/api/pay/miniapp'); + } + +} \ No newline at end of file diff --git a/packagist/unify_payment/src/Modules/Pay/OfficialAccountClient.php b/packagist/unify_payment/src/Modules/Pay/OfficialAccountClient.php new file mode 100644 index 0000000..6eebb80 --- /dev/null +++ b/packagist/unify_payment/src/Modules/Pay/OfficialAccountClient.php @@ -0,0 +1,34 @@ +payload = array_merge($this->payload, $order->toArray()); + $this->payload['sign'] = $this->generateSign($this->payload); + + return $this->request('post', '/index.php/api/pay/mp'); + } +} \ No newline at end of file diff --git a/packagist/unify_payment/src/Unify.php b/packagist/unify_payment/src/Unify.php new file mode 100644 index 0000000..83f2622 --- /dev/null +++ b/packagist/unify_payment/src/Unify.php @@ -0,0 +1,24 @@ +publishes([ + __DIR__ . '/../config/unify.php.php' => config_path('unify.php'), + ]); + } + + /** + * Registering Application Services. + * + * @return void + */ + public function register(): void + { + $this->mergeConfigFrom( + __DIR__ . '/../config/unify.php', + 'unify' + ); + + $this->app->singleton('unify', function () { + return new Factory(config('unify')); + }); + } +} \ No newline at end of file diff --git a/packagist/unify_payment/tests/CommonTest.php b/packagist/unify_payment/tests/CommonTest.php new file mode 100644 index 0000000..d80d0f6 --- /dev/null +++ b/packagist/unify_payment/tests/CommonTest.php @@ -0,0 +1,94 @@ +getConfig())->order->setMockHandler([$mockHandler])->query($order); + + $this->assertArrayHasKey('response', $response); + $this->assertEquals([ + 'orderNo', + 'payAccount', + 'transNo', + 'status', + 'statusDesc', + 'amount', + 'tag', + 'merchantId', + 'merchantName', + 'channelId', + 'channelName' + ], array_keys($response['response'])); + } + + public function testOrderClose() + { + $order = new CloseOrder('FD2022080310000055123451223'); + + $mockHandler = new CloseOrderHandler(true); + $response = Factory::common($this->getConfig())->order->setMockHandler([$mockHandler])->close($order); + + $this->assertArrayHasKey('response', $response); + $this->assertEquals([ + 'orderNo', + 'closeTime', + 'merchantId', + 'merchantName', + 'channelId', + 'channelName' + ], array_keys($response['response'])); + } + + public function testOrderRefund() + { + $order = new RefundOrder('FD2022080310000055123451223', 'FD2022080310000055123451223_12345', 0.01, 'Test Refund'); + + $mockHandler = new RefundOrderHandler(true); + $response = Factory::common($this->getConfig())->order->setMockHandler([$mockHandler])->refund($order); + + $this->assertArrayHasKey('response', $response); + $this->assertEquals([ + 'orderNo', + 'refundNo', + 'refundAmount', + 'refundTime', + 'merchantId', + 'merchantName', + 'channelId', + 'channelName' + ], array_keys($response['response'])); + } + + public function testOrderConfirmForEx() + { + $order = new ConfirmOrderForEx('FD2022080310000055123451223', 'ZSD0002974832', 'ow8NuxN_ALO2MzSvzj19hyxWjCVY', '4200067674202302063709953797'); + + $mockHandler = new ConfirmOrderForExHandler(true); + $response = Factory::common($this->getConfig())->order->setMockHandler([$mockHandler])->confirmForEx($order); + + $this->assertArrayHasKey('response', $response); + $this->assertEquals([ + 'orderNo', + 'hisSerialNo', + 'confirmTime', + 'merchantId', + 'merchantName', + 'channelId', + 'channelName' + ], array_keys($response['response'])); + } +} \ No newline at end of file diff --git a/packagist/unify_payment/tests/PayTest.php b/packagist/unify_payment/tests/PayTest.php new file mode 100644 index 0000000..089ed70 --- /dev/null +++ b/packagist/unify_payment/tests/PayTest.php @@ -0,0 +1,52 @@ +getConfig())->mini->setMockHandler([$mockHandler])->jsapi($order); + + $this->assertArrayHasKey('response', $response); + $this->assertEquals([ + 'appId', + 'timeStamp', + 'nonceStr', + 'package', + 'signType', + 'paySign', + 'merchantId', + 'merchantName', + 'channelId', + 'channelName' + ], array_keys($response['response'])); + } + + public function testOfficialAccountJsapi() + { + $order = new CreateOrder('当天挂号', 'FD2022080310000055123451223', 0.01, '1', '002197|公众号', 'A', 'ovIGQwOy1e-Zptyug20l5hqI0P5Q', 'https://www.baidu.com'); + + $mockHandler = new CreateOrderHandler(true); + $response = Factory::pay($this->getConfig())->official->setMockHandler([$mockHandler])->jsapi($order); + + $this->assertArrayHasKey('response', $response); + $this->assertEquals([ + 'appId', + 'timeStamp', + 'nonceStr', + 'package', + 'signType', + 'paySign', + 'merchantId', + 'merchantName', + 'channelId', + 'channelName' + ], array_keys($response['response'])); + } +} \ No newline at end of file diff --git a/packagist/unify_payment/tests/TestCase.php b/packagist/unify_payment/tests/TestCase.php new file mode 100644 index 0000000..3de6c6a --- /dev/null +++ b/packagist/unify_payment/tests/TestCase.php @@ -0,0 +1,17 @@ + + + + + tests/Unit + + + tests/Feature + + + + + app + + + + + + + + + + + + + + + + diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..49c0612 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 0000000..3aec5e2 --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,21 @@ + + + Options -MultiViews -Indexes + + + RewriteEngine On + + # Handle Authorization Header + RewriteCond %{HTTP:Authorization} . + RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + + # Redirect Trailing Slashes If Not A Folder... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_URI} (.+)/$ + RewriteRule ^ %1 [L,R=301] + + # Send Requests To Front Controller... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.php [L] + diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..e69de29 diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..947d989 --- /dev/null +++ b/public/index.php @@ -0,0 +1,17 @@ +handleRequest(Request::capture()); diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..eb05362 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/resources/css/app.css b/resources/css/app.css new file mode 100644 index 0000000..b5c61c9 --- /dev/null +++ b/resources/css/app.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/resources/js/app.js b/resources/js/app.js new file mode 100644 index 0000000..e59d6a0 --- /dev/null +++ b/resources/js/app.js @@ -0,0 +1 @@ +import './bootstrap'; diff --git a/resources/js/bootstrap.js b/resources/js/bootstrap.js new file mode 100644 index 0000000..5f1390b --- /dev/null +++ b/resources/js/bootstrap.js @@ -0,0 +1,4 @@ +import axios from 'axios'; +window.axios = axios; + +window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; diff --git a/resources/views/welcome.blade.php b/resources/views/welcome.blade.php new file mode 100644 index 0000000..979e82a --- /dev/null +++ b/resources/views/welcome.blade.php @@ -0,0 +1,176 @@ + + + + + + + Laravel + + + + + + + @if (file_exists(public_path('build/manifest.json')) || file_exists(public_path('hot'))) + @vite(['resources/css/app.css', 'resources/js/app.js']) + @else + + @endif + + + + + diff --git a/routes/api.php b/routes/api.php new file mode 100644 index 0000000..5e7b6b6 --- /dev/null +++ b/routes/api.php @@ -0,0 +1,41 @@ +group(function() { + + Route::post('login', [AuthController::class, 'login']); + Route::any('unauthorized', [AuthController::class, 'unauthorized'])->name('login'); + + Route::middleware(['auth:sanctum'])->group(function() { + // 患者模块 + Route::prefix('Patient')->group(function () { + Route::get('/', [PatientController::class, 'lists']); + Route::get('/{patient_id}', [PatientController::class, 'details']); + Route::post('/create', [PatientController::class, 'create']); + Route::post('/bind', [PatientController::class, 'bind']); + Route::post('/{patient_id}/default', [PatientController::class, 'setDefault']); + Route::delete('/{patient_id}/delete', [PatientController::class, 'delete']); + }); + + // 挂号模块 + Route::prefix('Registration')->group(function () { + Route::get('/dept', [ScheduleController::class, 'dept']); + Route::get('/doctor', [ScheduleController::class, 'doctor']); + + Route::get('/{patient_id}/record', [RecordController::class, 'lists']); + Route::post('/{patient_id}/record/{serial_no}/refund', [RecordController::class, 'refund']); + }); + + // 缴费详情项目 + Route::prefix('Dictionary')->group(function () { + Route::get('/', [ItemController::class, 'lists']); + Route::get('/{type_id}', [ItemController::class, 'details'])->where('type_id', '[0-9]+');; + }); + }); +}); diff --git a/routes/console.php b/routes/console.php new file mode 100644 index 0000000..eff2ed2 --- /dev/null +++ b/routes/console.php @@ -0,0 +1,8 @@ +comment(Inspiring::quote()); +})->purpose('Display an inspiring quote')->hourly(); diff --git a/routes/web.php b/routes/web.php new file mode 100644 index 0000000..86a06c5 --- /dev/null +++ b/routes/web.php @@ -0,0 +1,7 @@ +get('/'); + + $response->assertStatus(200); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..fe1ffc2 --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,10 @@ +assertTrue(true); + } +} diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..421b569 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,11 @@ +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel({ + input: ['resources/css/app.css', 'resources/js/app.js'], + refresh: true, + }), + ], +});